URL的组成

URL地址由协议头、服务器地址、文件路径三部分组成

协议头(Protocol Head)

协议头指定使用的传输协议,用于告诉浏览器如何处理将要打开的文件。不同的协议表示不同的资源查找以及传输方式。网络上常用的协议如表1所示。

常见协议 代表类型 实例
file 访问本地计算机的资源 file:///Users/itcast/Desktop/basic.html
ftp 访问共享主机的文件资源 ftp://ftp.baidu.com/movies
http 超文本传输协议, 访问远程网络资源 http://image.baidu.com/channel/wallpaper
https 安全的ssl加密传输协议,访问远程网络资源 https://image.baidu.com/channel/wallpaper
mailto 访问电子邮件地址 mailto:null@208023732.cn

表1 URL常见的协议
其中最常用的是HTTP协议和HTTPS协议,分别由协议头http和https指定。

服务器地址(Hostname或者IP)和端口(Port)

  • 服务器地址指存放资源的服务器的主机名或者IP地址,其目的在于标识互联网上的唯一一台计算机,通过这个地址来找到这台计算机。
  • 端口是在地址和冒号后面的数字,用于标识在一台计算机上运行的不同程序。每个网络程序,都对应一个或多个特定的端口号,例如HTTP程序的默认端口号为80,HTTPS程序的默认端口号为443。
  • IP地址被用来给Internet上的每台电脑一个编号,但是IP地址不容易记忆,而且服务器的物理IP地址是有可能发生改变的。为此,人们又发明了域名来替代IP地址访问服务器的网站。例如,使用百度公司所在的IP地址“http://180.97.33.107”可以打开百度的首页,但是这个地址不易记忆,不如使用域名网址http://www.baidu.com访问来的方便。

路径(Path)

路径是由0或者多个“/”符号隔开的字符串,一般用于指定本次请求的资源在服务器中的位置。

爬取第一个网站

目标网站: 百度首页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import urllib.request

url = "https://www.baidu.com/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 "
"Safari/537.36"
}
# 构建请求对象
request = urllib.request.Request(url=url, headers=headers)
# 发送请求
response = urllib.request.urlopen(request)
# 获取响应内容
content = response.read().decode("utf-8")
# 输出状态码
print(response.getcode())

结果:

1
200

常见http状态码

https://www.runoob.com/http/http-status-codes.html

Post请求百度翻译

  • 构建请求对象即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import urllib.request
    import urllib.parse
    import json

    url = 'https://fanyi.baidu.com/sug'
    data = {
    'kw': 'hello',
    }
    # 请求头
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 '
    'Safari/537.36'
    }
    # 对post请求的data参数进行编码
    data = urllib.parse.urlencode(data).encode('utf-8')

    # 构建请求对象
    request = urllib.request.Request(url=url, data=data, headers=headers)
    # 发送请求
    response = urllib.request.urlopen(request)
    # 获取响应数据
    res = response.read().decode()
    # 将json字符串转换为python类型
    res = json.loads(res)
    print(res)
  • 运行结果

爬取指定城市肯德基的相关信息

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import urllib.request
import urllib.parse
import json


def create_request(page):
"""
创建请求对象
:param page: 页码
:return: 请求对象
"""
baseurl = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
data = {
'cname': '柳州',
'pid': '',
'pageIndex': page,
'pageSize': '10',
}
data = urllib.parse.urlencode(data).encode('utf-8')
headers = {
'Cookie': '自己的Cookie',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/114.0.0.0 Safari/537.36'
}
request = urllib.request.Request(url=baseurl, data=data, headers=headers)
return request


def get_content(request):
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
return content


def down_load(page, content):
with open('D:/project_code_file/python/pythonSpider/data/' + '柳州肯德基_' + str(page) + '.json', 'w', encoding='utf-8') as f:
f.write(content)


if __name__ == '__main__':
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入终止页码:'))
for page in range(start_page, end_page + 1):
request = create_request(page)
content = get_content(request)
down_load(page, content)

结果:

Cookie的简单使用

cookie简介


Cookie 是通过浏览器将服务器返回的数据保存在本地的一小块数据(一般小于4kb)。当浏览器发送请求且浏览器存在 Cookie 时,浏览器会自动在请求头携带上 Cookie 数据。引入 Cookie 的意义是因为 HTTP 的请求是无状态的,如:浏览器发出的请求服务器没办法区分浏览器用户身份以及用户的相关操作状态(可以通过 Cookie 传递这些信息)。最开始 Cookie 被作为唯一的存储手段,但是因为浏览器的每次请求都会携带上 Cookie,会带来额外的开销,而且存储量比较小,所以后来浏览器推出了新的 Api(web stoeage Api和 indexedDb)。


cookie使用场景

  • 会话状态管理(如用户登录状态、及其他需要记录的信息)
  • 个性化设置(如用户自定义设置)
  • 浏览器追踪行为(如追踪分析用户行为)

实例

使用cookie进入微博登录状态的页面(获取的cookie是以及登录过微博之后的cookie)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import urllib.request
import urllib.parse

url = 'https://weibo.com/u/5887067753'

headers = {
# 'Accept': 'application/json, text/plain, */*',
# 'Accept-Language': 'zh-CN,zh;q=0.9',
# 'Client-Version': 'v2.40.78',
#
'Cookie': '自己的Cookie',
'Referer': 'https://weibo.com/set/index',
# 'Sec-Ch-Ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
# 'Sec-Ch-Ua-Mobile': '?0',
# 'Sec-Ch-Ua-Platform': '"Windows"',
# 'Sec-Fetch-Dest': 'empty',
# 'Sec-Fetch-Mode': 'cors',
# 'Sec-Fetch-Site': 'same-origin',
# 'Server-Version': ' v2023.06.30.4',
# 'Traceparent': ' 00-0144ec36f5743fae1c9d181a8daa019e-96774156e66b2ade-00',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 '
'Safari/537.36',
# 'X-Requested-With': 'XMLHttpRequest',
# 'X-Xsrf-Token': 'cbEMLmhIdocB-Wj023eGlz65',
}
# 请求对象定制
request = urllib.request.Request(url=url, headers=headers)
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(request)
# 获取响应数据
content = response.read().decode('gb2312')

with open('D:/project_code_file/python/pythonSpider/html/weibo.html', 'w', encoding='gb2312') as fp:
fp.write(content)

效果:

代理服务器

代理的常用功能

  • 突破自身ip的访问限制,访问国外站点
  • 访问一些单位或团体内部资源
  • 提高访问速度
  • 隐藏真实ip

代码配置代理

  • 创建Reuqest对象
  • 创建ProxyHandler对象
  • 用handler对象创建opener对象
  • 使用opener.open函数发送请求

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import urllib.request

url = 'https://www.baidu.com/s?ie=UTF-8&wd=ip'

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 '
'Safari/537.36'
}
proxies = {
'http': 'ip:port',
}
# 请求对象的定制
request = urllib.request.Request(url=url, headers=headers)
# 模拟浏览器访问服务器
# response = urllib.request.urlopen(request)
handler = urllib.request.ProxyHandler(proxies=proxies)
opener = urllib.request.build_opener(handler)
response = opener.open(request)
# 获取响应信息
content = response.read().decode('utf-8')

with open('D:/project_code_file/python/pythonSpider/html/ip.html', 'w', encoding='utf-8') as fp:
fp.write(content)

Xpath的简单使用

  1. 使用到的python模块为lxml,一般是:from lxml import etree
  2. 结合浏览器插件:XPath Helper使用(具体安装自己查),元素定位很方便,敏感肌也能用

长这样:

  1. 虽然有XPath Helper,基本语法还是需要学一学的

下面针对这个html文件,进行简单的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<ul>
<li id = "l1" class="c1">百度</li>
<li id = "l2">谷歌</li>
<li id = "c3">必应</li>
<li id = "c4">搜狗</li>
</ul>
<ul>
<li id = "l3">北京</li>
<li id = "l4">上海</li>
<li>广州</li>
<li>深圳</li>
</ul>
</body>
</html>

python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from lxml import etree

# xpath 解析
# 1. 本地文件 etree.parse()
# 2. 服务器响应的数据 etree.HTML()

tree = etree.parse('D:/project_code_file/python/pythonSpider/html/17xpath的基本使用.html')

# tree.xpath(xpath路径)
# 1. 获取所有的li标签
li_list = tree.xpath('//body//li/text()')
print(li_list)

# 2. 查找所有有id属性的li标签
li_list = tree.xpath('//li[@id]/text()')
print(li_list)

# 3. 查找id为l1的li标签,注意引号问题
li_list = tree.xpath('//li[@id="l1"]/text()')
print(li_list)

# 4. 查找到id为l1的li标签的class属性值
li_list = tree.xpath('//li[@id="l1"]/@class')
print(li_list)

# 5. 查询id中包含'l'的li标签
li_list = tree.xpath('//li[contains(@id,"l")]/text()')
print(li_list)

requests模块的使用

简介

一个类型,六个属性
一个类型指的是get方法返回的是一个Response类型的对象
六个属性是值Response中的六个属性,分别是


  • 设置响应的编码格式:response.encoding = ‘编码格式’
  • 返回网页内容:response.text
  • 返回网页的url:response.url
  • 返回网页的二进制数据:response.content
  • 返回响应头:response.headers

get请求简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests

url = 'https://www.baidu.com/s?'

headers = {
# 填自己的cookie
'Cookie': 'Cookie',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 '
'Safari/537.36',
}
data = {
'wd': '北京'
}
# url: 请求的url地址 params: 请求的参数 kwargs: 字典
response = requests.get(url=url, params=data, headers=headers)

response.encoding = 'utf-8'
response_text = response.text
print(response_text)

post请求简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import json
url = 'https://fanyi.baidu.com/sug'

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
}

data = {
'kw': 'eye'
}

# url为请求的地址,data为请求的参数,kwargs为请求头
response = requests.post(url=url, data=data, headers=headers)
content = response.text
dict_content = json.loads(content)
print(dict_content)

综合案例(登录古诗文网)



  1. 抓取登录接口常用方法:打开目标网站的登录界面>>>F12>>>Network>>>在网页输入错误的密码并且登录>>>找登录接口>>>分析
  1. 开始干活:分析Form Data的数据,第一个__VIEWSTATE和第二份__VIEWSTATEGENERATOR数据看不懂,不知道哪里来的,先不着急,往下看,from应该是固定的,这个没问题,再往下看,只有code是随机的,那我们要怎样获取code呢?很简单,验证码是在网页中的,那我们把这张图片弄到手解析不就完了?
  1. 这下好了,验证码的URL有了,还愁获取不到验证码信息吗!再来解决数据1和数据2的问题,一般这种找不到的信息都在网页源码当中ctrl f自己找一找看看
  1. 简单示例, 没有使用到验证码图片解析,是比较笨的用手输入的,学过的朋友可以自己写一个来用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 通过登录,然后进入到主界面
import requests
from bs4 import BeautifulSoup
post_url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'

# 这是登录页面的url地址
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
code_url = 'https://so.gushiwen.cn/RandCode.ashx'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 '
'Safari/537.36'
}

response = requests.get(url=url, headers=headers)
content = response.text
# print(content)

soup = BeautifulSoup(content, 'lxml')
# 获取__VIEWSTATE和__VIEWSTATEGENERATOR
viewstate = soup.select('#__VIEWSTATE')[0].attrs.get('value')
viewstategenerator = soup.select('#__VIEWSTATEGENERATOR')[0].attrs.get('value')

# 通过session()方法,获取验证码
session = requests.session()
content_code = session.get(url=code_url, headers=headers).content
with open('D:\project_code_file\python\pythonSpider\img\code.jpg', 'wb') as f:
f.write(content_code)
code = input("请输入验证码:")

post_data = {
'__VIEWSTATE': viewstate,
'__VIEWSTATEGENERATOR': viewstategenerator,
'from': 'http://so.gushiwen.cn/user/collect.aspx',
'email': 'your email',
'pwd': 'your passwd',
'code': code,
'denglu': '登录'
}

response_post = session.post(url=post_url, headers=headers, data=post_data)

content_post = response_post.text

with open('D:\project_code_file\python\pythonSpider\html\gushiwen.html', 'w', encoding='utf-8') as f:
f.write(content_post)


selenium模块的使用

简介

Selenium 是一种开源工具,用于在 Web 浏览器上执行自动化测试(使用任何 Web 浏览器进行 Web 应用程序测试)
也可以应用于爬虫,对于一些数据,类似购物网站的限时秒杀部分的数据,普通的爬虫程序无法获取该部分数据,当我们使用真实的浏览器去访问的时候就能获取到。而selenium就能帮助我们实现这一功能

环境准备

  • 需要对应的浏览器驱动,这里使用的是chromedriverchromedriver.exe 点我获取驱动
  • 驱动直接拖入项目文件夹下,放在别处要注意编码时的路径

需求

访问百度,并且在百度的搜索框上输入“周杰伦”,并且点击搜索按钮执行该操作

编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


# 封装成函数
def share_chrome():
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')

path = r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
chrome_options.binary_location = path

browser = webdriver.Chrome(chrome_options=chrome_options)
return browser


url = 'https://www.baidu.com'
browser = share_chrome()
browser.get(url)

# 在输入框输入周杰伦
text = browser.find_element_by_id('kw')
text.send_keys('周杰伦')
# 点击搜索按钮
btn = browser.find_element_by_id('su')
btn.click()
sleep(2)
# 拍照
browser.save_screenshot('baidu.png')

说明

此过程是隐式的。如何证明已经完成了此过程?具体可以看第29-30行代码

scrapy爬虫框架入门

简介

  1. scrapy是什么?
  • scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘、信息采集、等方面
  1. scrapy的安装
  • pip install scrapy
  • 如果出现错误,十有八九应该是没配置twisted的问题
  1. scrapy项目的创建
  • 创建爬虫项目

scrapy startproject 项目名(不能以数字开头,也不能包含中文)

  • 创建爬虫文件,要在项目的spiders文件夹下创建爬虫文件

cd .\项目名称\项目名称\spiders

scrapy genspider 爬虫文件的名字(不用带.py后缀) 爬取网站的域名

  • 运行爬虫文件

scrapy crawl 爬虫文件的名字

注意:要在spiders文件夹下执行

  • scrapy项目文件结构

scrapy_demo 项目的名字

scrapy_demo 项目的名字,也是python软件包
spiders 爬虫文件夹,也是python软件包
init.py
demo.py 爬虫文件,核心功能文件
init.py
items.py 用于存储爬取的数据
middlewares.py 中间件,用来处理请求和响应
pipelines.py 管道文件,用来处理爬取的数据
settings.py 配置文件 用来配置项目的一些信息,比如:UA
scrapy.cfg

scrapy框架结构

  1. scrapy引擎

自动运行,不用管

  1. scrapy下载器

引擎给到对象和数据,并履行数据下载的职责

  1. spiders

spiders类定义了如何爬取某个网站,包括了爬取的方式,以及如何从网页的内容中提取结构化数据。换句话说,spiders是我们定义爬取动作以及分析某个网页的地方

  1. scrapy 调度器

爬虫框架的系统调度,不用管

  1. scrapy管道

最终处理数据的管道,会预留接口供我们处理数据

  1. 工作原理

爬虫文件中常用的方法、属性

首先核心爬虫文件中最核心的东西我个人觉得应该是response对象,response对象中包含了爬取网页的所有信息。获取自己想要的数据,就得熟悉该对象的属性还有方法

  1. 常用属性
  • response.body 获取响应体,二进制数据
  • response.text 获取响应体的文本
  • response.url 获取url
  • response.status 获取响应状态码
  • response.encoding 获取响应体的编码
  • response.request 获取请求对象
  • response.headers 获取响应头
  1. 常用方法
  • response.xpath(xpath路径) 使用xpath路径查询特定元素,返回一个selector列表对象

selector对象中常用的有extract()方法,该方法可以提取selector对象的值,使用xpath请求到的对象是一个selector对象,需要进一步使用extract()方法拆解成unicode字符串。同样的,常用的还有extract_first()方法,见名知意,该方法会提取selector对象中的第一个值。需要注意的是,每一个selector对象可以多次调用xpath方法。

  1. yield
  • 带有 yield 的函数不再是一个普通函数,而是一个生成器generator,可用于迭代
  • yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面(右边)的值。重点是:下一次迭代 时,从上一次迭代遇到的yield后面的代码(下一行)开始执行
  • 简要理解:yield就是 return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始

案例(当当网)

需求

获取该网页书的图片还有书名、价格https://category.dangdang.com/cp01.01.02.00.00.00.html
实现多条管道下载
多页数据下载

操作步骤

  • 编码

dangdang.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import scrapy
from scrapy_dangdang_41.items import ScrapyDangdang41Item


class DangdangSpider(scrapy.Spider):
name = 'dangdang'
# 如果是多页下载的话,那么必须调整的是allowed_domains的范围。一般情况下只写域名
allowed_domains = ['category.dangdang.com']
start_urls = ['https://category.dangdang.com/cp01.01.02.00.00.00.html']

base_url = 'https://category.dangdang.com/pg'
page_num = 1
tail_url = '-cp01.01.02.00.00.00.html'

def parse(self, response):

# pic || //ul[@id="component_59"]/li//img/@src 图片的xpath路径
# 注意网站的懒加载,后面的图片的src属性是data-original,而不是src
li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
src = li.xpath('.//img/@data-original').extract_first()
if src:
src = src
else:
src = li.xpath('.//img/@src').extract_first()
name = li.xpath('.//img/@alt').extract_first()
price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()

book = ScrapyDangdang41Item(src=src, name=name, price=price)
yield book # 获取一个book就交给一个pipeline处理

if self.page_num < 100:
self.page_num += 1
url = self.base_url + str(self.page_num) + self.tail_url
# 怎么去调用parse方法
# scrapy.Request就是scrapy的get请求
# url:请求的url地址
# callback:回调函数,就是指定请求成功之后的回调函数,注意不需要加()
yield scrapy.Request(url=url, callback=self.parse)

items.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class ScrapyDangdang41Item(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 通俗的说你要下载的数据都有什么

src = scrapy.Field() # 图片
name = scrapy.Field() # 名称
price = scrapy.Field() # 价格

pipelines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
import requests
# 如果想使用管道的话,就要在settings.py中开启管道
class ScrapyDangdang41Pipeline:
# item 就是爬虫文件中yield过来的item对象
def open_spider(self, spider):
print('爬虫开始了')
self.fp = open('D:\\project_code_file\\python\\pythonSpider\\data\\book.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
# 以下这种模式不推荐使用,因为每次都会打开文件,效率低
# 1. write方法必须要写一个字符串,所以要把item转换成字符串
# 2. w模式,每次都会清空文件,所以要使用a模式
# with open('D:\\project_code_file\\python\\pythonSpider\\data\\book.json', 'a', encoding='utf-8') as f:
# f.write(str(item) + '\n')
# 高效的写法
self.fp.write(str(item) + '\n')
return item

def close_spider(self, spider):
print('爬虫结束了')
self.fp.close()


# 多条管道开启
# 1. 定义管道类
# 2. 在settings.py中开启管道
# 'scrapy_dangdang_41.pipelines.DangDangDownloadPipeline': 301

class DangDangDownloadPipeline:
def process_item(self, item, spider):
# 1.获取图片的链接
src = "http:" + item['src']
# 2.发送请求
res = requests.get(src)
# 3.获取图片的二进制数据
img_data = res.content
# 4.生成图片名称
img_name = item['name'] + '.jpg'
# 5.拼接图片的保存路径
img_path = 'D:\\project_code_file\\python\\pythonSpider\\img\\books\\' + img_name
# 6.保存图片
with open(img_path, 'wb') as f:
f.write(img_data)
return item

settings.py

把管道开启,并且把ROBOTSTXT_OBEY注释掉,或者设置为False即可
管道开启方法:将Configure item pipelines那部分注释去掉就好,注意观察这里新增了一条管道,对应pipelines.py新增的类

1
2
3
4
5
6
7
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 管道可以有很多个 管道有优先级 优先级范围是0-1000 值越小优先级越高
'scrapy_dangdang_41.pipelines.ScrapyDangdang41Pipeline': 300,
'scrapy_dangdang_41.pipelines.DangDangDownloadPipeline': 301,
}
  • 运行项目(注意一定要在项目的spiders目录下执行该命令!)

    scrapy crawl dangdang

结果:(还没爬完…..做个测试即可)

CrawlSpider

简介

继承自scrapy.Spider
区别:CrawlSpider可以自定义规则,再解析html内容的时候,可以根据链接规则,提取出指定的链接,然后再向这些链接发送请求,意思是爬取了网页之后,需要提取链接在此爬取,使用CrawlSpider是非常合适的

链接提取器

1
2
3
4
scrapy.linkextractors.LinkExtractor(
allow = (正则表达式 提取符合正则的链接)
restrict_xpaths = (xpath, 提取符合xpath规则的链接)
)

使用样例

  • 正则: link = LinkExtractor(allow=r’正则表达式’)
  • xpath:link = LinkExtractor(restrict_xpaths=r’xpath表达式’)

提取链接

link.extract_links(response)

注意事项

follow = bool
bool表示是否跟进,就是是否按照提取链接规则进行提取
原理图

案例(CrawlSpider 读书网)

需求

读书网数据入库,使用到mysql数据库

操作步骤

  • 创建项目

    scrapy startproject scrapy_readbook_43

  • 跳转到spiders目录下

    cd .\scrapy_readbook_43\scrapy_readbook_43\spiders

  • 创建爬虫文件

    scrapy gensipder -t crawl readbook www.dushu.com

  • 编码

readbook.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_readbook_43.items import ScrapyReadbook43Item

class ReadbookSpider(CrawlSpider):
name = 'readbook'
allowed_domains = ['www.dushu.com']
start_urls = ['https://www.dushu.com/book/1188_1.html']

rules = (
Rule(LinkExtractor(allow=r'/book/1188_\d+\.html'), callback='parse_item', follow=True),
)

def parse_item(self, response):
img_list = response.xpath('//div[@class="bookslist"]//img')
author_list = response.xpath('//div[@class="book-info"]/p[1]/text()').extract()
i = 0
for img in img_list:
photo_url = img.xpath('./@data-original').extract_first()
book_name = img.xpath('./@alt').extract_first()
author = author_list[i]
i += 1
book = ScrapyReadbook43Item(photo_url=photo_url, book_name=book_name, author=author)
yield book

items.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class ScrapyReadbook43Item(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 图片和名字还有作者
photo_url = scrapy.Field()
book_name = scrapy.Field()
author = scrapy.Field()

pipelines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
from pymysql import Connection


class ScrapyReadbook43Pipeline:
def open_spider(self, spider):
self.fp = open('readbook.json', 'w', encoding='utf-8')

def process_item(self, item, spider):
self.fp.write(str(item) + '\n')
return item

def close_spider(self, spider):
self.fp.close()



class MysqlPipeline:
def __init__(self):
self.cursor = None
self.connect = None

def open_spider(self, spider):
self.connect_database()

def connect_database(self):
self.connect = Connection(
host='localhost',
port=3306,
user='root',
password='123456',
autocommit=True,
# db='spider01',
# charset='utf8'
)
self.connect.select_db('spider01')
def process_item(self, item, spider):
self.cursor = self.connect.cursor()
sql = "insert into books(book_name, author, photo_url) values('{}', '{}', '{}')".format(item['book_name'], item['author'], item['photo_url'])
self.cursor.execute(sql)
return item


def close_spider(self, spider):
self.cursor.close()
self.connect.close()
print("数据库连接关闭")

settings.py

开启管道即可

1
2
3
4
5
6
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'scrapy_readbook_43.pipelines.ScrapyReadbook43Pipeline': 300,
'scrapy_readbook_43.pipelines.MysqlPipeline': 301,
}

在settings.py中也可以指定日志的显示形式
比如加入LOG_FILE = '文件名.log'
项目运行的时候就不会在控制台输出一堆东西了,而是以log文件的形式保存了起来

  • 运行

    scrapy crawl readbook

结果

数据库

json文件