Scrapy
安装
Windows 安装方式
升级 pip 版本:
1pip install --upgrade pip
通过 pip 安装 Scrapy 框架:
Ubuntu 安装方式
安装非 Python 的依赖:
1sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
通过 pip 安装 Scrapy 框架:
Mac OS 安装方式
pip 版本必须转 22+,升级 pip 版本
1pip3 install --upgrade pip
使用清华源下载
1pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy
注意:在 mac 中使用 scrapy 指令必须在前面加上 python3 -m
新建项目
在开始爬取之前,必须创建一个新的 Scrapy 项目。进入自定义的项目目录中,运行下列命令:
1scrapy startproject mySpider
其中, mySpider 为项目名称,可以看到将会创建一个 mySpider 文件夹,目录结构大致如下:
下面来简单介绍一下各个主要文件的作用:
1mySpider/
2 scrapy.cfg
3 mySpider/
4 __init__.py
5 items.py
6 pipelines.py
7 settings.py
8 spiders/
9 __init__.py
10 ...
这些文件分别是:
- scrapy.cfg: 项目的配置文件。
- mySpider/: 项目的 Python 模块,将会从这里引用代码。
- mySpider/items.py: 项目的目标文件。
- mySpider/pipelines.py: 项目的管道文件。
- mySpider/settings.py: 项目的设置文件。
- mySpider/spiders/: 存储爬虫代码目录。
创建爬虫文件
1、cd 进入 spiders
文件夹中
1cd mySpider\spiders\spiders
2、创建爬虫文件
1# scrapy genspider 文件名 网页地址
2scrapy genspider test www.baidu.com
3、test.py
1import scrapy
2
3class TestSpider(scrapy.Spider):
4 name = 'test'
5 allowed_domains = ['www.baidu.com']
6 start_urls = ['https://www.baidu.com/']
7
8 def parse(self, response):
9 pass
运行爬虫代码
注意:有的网站会有 robots 协议,这是一个君子协议,scrapy 默认是启动遵守的,如果想要爬取需要关闭
在settings.py
文件中
1# Obey robots.txt rules
2ROBOTSTXT_OBEY = True 注释该行
语法
response 对象
解析数据返回的对象
-
response.body
:响应返回页面已二进制格式的内容
-
response.text
:响应返回页面已字符串格式的内容
-
response.url
:响应返回页面 url
-
response.status
:响应返回 ajax 请求状态码
-
response.xpath()
:(常用) 使用 xpath 路径查询特定元素,返回一个selector
列表对象
-
response.css()
:使用css_selector
查询元素,返回一个selector
列表对象
- 获取内容 :
response.css('#su::text').extract_first()
- 获取属性 :
response.css('#su::attr(“value”)').extract_first()
selector 对象
通过xpath
方法调用返回的是seletor
列表
- 提取
selector
对象的值
- 如果提取不到值,那么会报错
- 使用 xpath 请求到的对象是一个
selector
对象,需要进一步使用extract()
方法拆 包,转换为unicode
字符串
- 提取
seletor
列表中的第一个值
- 如果提取不到值,会返回一个空值
- 返回第一个解析到的值,如果列表为空,此种方法也不会报错,会返回一个空值
xpath() css()
注意:每一个selector
对象可以再次的去使用xpath
或者css
方法
使用管道封装
1、items.py 在项目目标文件中定义
1class Scrapy01TestItem(scrapy.Item):
2 # define the fields for your item here like:
3 # name = scrapy.Field()
4 # src = scrapy.Field()
5 name = scrapy.Field()
6 href = scrapy.Field()
2、爬虫住文件
1import scrapy
2from scrapy_01_test.items import Scrapy01TestItem
3
4class TestSpider(scrapy.Spider):
5 name = 'test'
6 allowed_domains = ['bbs.mihoyo.com']
7 start_urls = ['https://bbs.mihoyo.com/ys/obc/channel/map/189/25?bbs_presentation_style=no_header']
8
9 def parse(self, response):
10 ul = response.xpath('//*[@id="__layout"]/div/div[2]/div[2]/div/div[1]/div[2]/ul/li/div/ul/li[1]/div/div/a')
11 for li in ul:
12 href = li.xpath('@href').extract_first()
13 name = li.xpath('.//div[2]/text()').extract_first()
14
15 # 调用Scrapy01TestItem将数据存储中目标文件中
16 # data = Scrapy01TestItem(href=href, name=name)
17
18 # 第二页的地址
19 url = 'https://bbs.mihoyo.com' + href
20
21 yield scrapy.Request(url, callback=self.parse_second, meta={
22 'href': href,
23 'name': name
24 })
25
26 # 每次循环得到的结果交给管道,如果是多个管道链接调用泽在最后一个执行的管道中 yield 最终的数据
27 # yield data
28
29 def parse_second(self, response):
30 src = response.xpath(
31 '//*[@id="__layout"]/div/div[2]/div[2]/div/div[1]/div[3]/div[3]/div[3]/div[1]/div[1]/div/ul[2]/li/img/@src').extract_first()
32 name = response.meta['name']
33 href = response.meta['href']
34
35 # 调用Scrapy01TestItem将数据存储中目标文件中
36 data = Scrapy01TestItem(src=src, href=href, name=name)
37 # 每次循环得到的结果交给管道,如果是多个管道链接调用泽在最后一个执行的管道中 yield 最终的数据
38 yield data
3、在settings.py
中开启管道
1# Configure item pipelines
2# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
3ITEM_PIPELINES = {
4 # 管道可以有多个
5 # scrapy_01_test.pipelines.Scrapy01TestPipeline 管道的类名路径
6 # 300 是管道的优先级,范围1-1000,值越小优先级越高
7 'scrapy_01_test.pipelines.Scrapy01TestPipeline': 300,
8}
4、pipelines.py 管道文件
1# Define your item pipelines here
2#
3# Don't forget to add your pipeline to the ITEM_PIPELINES setting
4# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
5
6# useful for handling different item types with a single interface
7import urllib.request
8
9from itemadapter import ItemAdapter
10
11# 必须在setings中开启管道才能使用
12class Scrapy01TestPipeline:
13 # 在爬虫文件执行之前调用一次
14 def __init__(self):
15 self.fb = None
16 self.initdata = []
17
18 def open_spider(self, spider):
19 pass
20
21 # itme 就是在yield后面的对象
22 def process_item(self, item, spider):
23 self.initdata.append(item)
24 return item
25
26 # 在爬虫文件执行之后调用一次
27 def close_spider(self, spider):
28 # w 模式每次执行都会打开文件覆盖之前的内容
29 self.fb = open('data.json', 'a', encoding='utf-8')
30 # write 方法必须写一个字符串
31 self.fb.write(str(self.initdata))
32 # 关闭
33 self.fb.close()
34
35# 多管道开始
36# 在 settings 中开启管道
37# 'scrapy_01_test.pipelines.DownLoadYS': 301,
38class DownLoadYS:
39 # itme 就是在yield后面的对象
40 def process_item(self, item, spider):
41 url = item.get('src')
42 filename = f'./img/{item.get("name")}.jpg'
43
44 urllib.request.urlretrieve(url=url, filename=filename)
45
46 return item
5、items 目标文件
1# Define here the models for your scraped items
2#
3# See documentation in:
4# https://docs.scrapy.org/en/latest/topics/items.html
5
6import scrapy
7
8class Scrapy01TestItem(scrapy.Item):
9 # define the fields for your item here like:
10 # name = scrapy.Field()
11 src = scrapy.Field()
12 name = scrapy.Field()
13 href = scrapy.Field()
创建爬虫文件
1# scrapy genspider 文件名 网页地址
2scrapy genspider -t crawl test www.baidu.com
Scrapy CrawlSpider,继承自Spider
, 爬取网站常用的爬虫,其定义了一些规则(rule)方便追踪或者是过滤link
。 也许该 spider 并不完全适合您的特定网站或项目,但其对很多情况都是适用的。 因此您可以以此为基础,修改其中的方法,当然您也可以实现自己的spider
。
class scrapy.contrib.spiders.CrawlSpider
CrawlSpider
继承自Spider
, 爬取网站常用的爬虫,其定义了一些规则(rule
)方便追踪或者是过滤 link。 也许该 spider 并不完全适合您的特定网站或项目,但其对很多情况都是适用的。 因此您可以以此为基础,修改其中的方法,当然您也可以实现自己的spider
。
除了从Spider
继承过来的(您必须提供的)属性外,其提供了一个新的属性:
一个包含一个(或多个) Rule
对象的集合(list)。 每个 Rule
对爬取网站的动作定义了特定表现。 Rule
对象在下边会介绍。 如果多个 rule 匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。
该 spider 也提供了一个可复写(overrideable
)的方法:
- parse_start_url(response)
当start_url
的请求返回时,该方法被调用。 该方法分析最初的返回值并必须返回一个 Item
对象或者 一个 Request
对象或者 一个可迭代的包含二者对象。
爬取规则
1class scrapy.contrib.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
link_extractor
是一个 Link Extractor
对象。 其定义了如何从爬取到的页面提取链接。
callback
是一个 callable 或 string(该 spider 中同名的函数将会被调用)。 从link_extractor
中每获取到链接时将会调用该函数。该回调函数接受一个 response 作为其第一个参数, 并返回一个包含 Item 以及(或) Request 对象(或者这两者的子类)的列表(list)。
当编写爬虫规则时,请避免使用 parse
作为回调函数。 由于 CrawlSpider
使用 parse 方法来实现其逻辑,如果 您覆盖了 parse 方法,crawl spider 将会运行失败。
cb_kwargs
: 包含传递给回调函数的参数(keyword argument)的字典。
follow
: 是一个布尔(boolean)值,指定了根据该规则从 response 提取的链接是否需要跟进。 如果 callback 为 None, follow 默认设置为 True ,否则默认为 False 。
process_links
: 是一个 callable 或 string(该 spider 中同名的函数将会被调用)。 从 link_extractor 中获取到链接列表时将会调用该函数。该方法主要用来过滤。
process_request
: 是一个 callable 或 string(该 spider 中同名的函数将会被调用)。 该规则提取到每个 request 时都会调用该函数。该函数必须返回一个 request 或者 None。 (用来过滤 request)
CrawlSpider 示例
1import scrapy
2from scrapy.spiders import CrawlSpider, Rule
3from scrapy.linkextractors import LinkExtractor
4
5class MySpider(CrawlSpider):
6 name = 'geek-docs'
7 allowed_domains = ['yiibai.com']
8 start_urls = ['https://www.yiibai.com/cplusplus/what-is-cpp.html']
9
10 rules = (
11 # 提取匹配 'yiibai.com/cplusplus' (但不匹配 'subsection.php') 的链接并跟进链接(没有callback意味着follow默认为True)
12 Rule(LinkExtractor(allow=('yiibai.com/cplusplus', ), deny=('subsection\.php', ))),
13
14 # 提取匹配 'item.php' 的链接并使用spider的parse_item方法进行分析
15 Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
16 )
17
18 def parse_item(self, response):
19 self.log('Hi, this is an item page! %s' % response.url)
20
21 item = scrapy.Item()
22 item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
23 item['name'] = response.xpath('//td[@id="item_name"]/text()').extract()
24 item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
25 return item
CrawlSpider
将从yiibai.com
的首页开始爬取,获取yiibai.com/cplusplus
以及item
的链接并,对后者调用 parse_item 方法。 当 item 获得返回response
时,将使用XPath
处理 HTML 并生成一些数据填入 Item 中。
日志信息和日志等级
日志级别:
- CRIRICAL:严重错误
- ERROR:一般错误
- WARNING:警告
- INFO:一级信息
- DEBUG:调试信息
默认日志等级是 DEBUG,只有出现 DEBUG 才会出现。
settings.py 文件设置:
- LOG_FILE:将屏幕显示的信息全部记录到文件中,屏幕不在显示,注意文件名一定是.log
- LOG_LEVEL: 设置日志的等级,就显示哪些,不显示哪些
POST 请求
1import scrapy
2
3class FySpider(scrapy.Spider):
4 name = 'fy'
5 # allowed_domains = ['www.baidu.com']
6 start_urls = ['https://fanyi.baidu.com/sug']
7
8 def start_requests(self):
9 data={
10 'kw':"beautiful"
11 }
12 for url in self.start_urls:
13 yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)
14
15 def parse(self, response):
16 print(response.text)