Scrapy
实现豆瓣图片爬取
使用
Scrapy
框架爬取豆瓣网站小姐姐图片资源(逃レ(゚∀゚;)ヘ=3=3=3)。
准备工作
Scrapy
框架安装
1 | pip3 install scrapy |
终端输入 scrapy
验证安装成功。
MongoDB
安装
1 | brew install mongodb |
终端输入 mongo
验证安装成功。
PyMongo
安装
1 | pip3 install pymongo |
验证安装:
1 | python3 |
创建项目
首先,确定我们爬取的目标,我们以 豆瓣上 gakki
的所有图片为例,从 https://movie.douban.com/celebrity/1018562/photos/
页面开始,爬取所有相关图片页的图片。然后开始创建项目:
创建一个 Scrapy
项目,项目初始文件可以直接用 scrpay
命令生成,命令如下:
1 | scrapy startproject gakkiDouban |
创建 Spider
Spider
是自己定义的类, Scrapy
用它来从网页里抓取内容,并解析抓取的结果。
Spider
继承 Scrapy
提供的 Spider
类 scrapy.Spider
,还要定义 Spider
的名称和起始请求,以及怎样处理爬取后的结果的方法。
使用命令行创建一个 Spider
,命令如下:
1 | cd gakkiDouban |
进入刚才创建的文件夹,然后执行 genspider
命令。第一个参数是 Spider
的名称,第二个参数是网站的域名。这里为 ?
和 =
添加了转义字符。
创建 Item
Item
是保存爬取数据的容器,它的使用方法和字典类似。不过,相比字典,Item
多了额外的保护机制,可以避免拼写错误或者定义字段错误。
创建 Item
需要继承 scrapy.Item
类,并且定义类型为 scrapy.Field
的字段。观察目标网站,我们需要获取的内容有 url
、page
。
定义 Item
,修改 item.py
如下:
1 | import scrapy |
这里定义了两个字段,接下来爬取时我们会使用到这个 Item
。
解析 Response
在创建 Spider
步骤时,我们看到有一个 parse
方法,它的参数 resposne
是 start_urls
里面的链接爬取后的结果。所以在 parse()
方法中,我们可以直接对 response
变量包含的内容进行解析,比如浏览器请求结果的网页源代码,或者进一步分析源代码的内容,或者找出结果中的链接而得到下一个请求。
这里我们先需要获得定义的字段内容 url
和 page
:
1 | def parse(self, response): |
这里的 extract_first()
方法来获取第一个元素, extract()
方法获取整个列表。
使用 Item
Item
可以理解为一个字典,不过在声明的时候需要实例化,然后依次用刚才解析的结果赋值给 Item
的每一个字段,最后将 Item
返回。
1 | from gakkiDouban.items import GakkidoubanItem |
后续 Request
获取了页面内容抓取之后,我们还需要从当前页中找到信息生成下一个请求,这样循环往复迭代,实现整站的爬取。
1 | def parse(self, response): |
这里我们获取了下一页的链接,然后调用 urljoin()
方法实现了下一个请求,这个方法在 URL
是相对地址时构造一个绝对地址,而如果是绝对地址又保持原样返回。
最后利用回调函数(callback
)再次进入 pares()
,就进入了下一页的爬取和解析。
运行
在 settings.py
中设置 user-agent
,伪装成浏览器:
1 | USER_AGENT ='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' |
这里因为域的问题修改一下 spider
文件,使其允许域名为豆瓣主目录,避免提取下一页时地址被过滤:
1 | class ImagesSpider(scrapy.Spider): |
终端进入项目爬虫目录运行:
1 | scrapy crawl images |
images
是我们上面自定义的 Spider
名称。
保存到文件
现在运行完 Scpary
后,我们只能在控制台看到输出结果,如果要将结果保存下来改怎么做呢?
如需保存成 Json
文件,可以执行如下命令:
1 | scrapy crawl images -o images.json |
这样,运行完成之后,项目内多了一个 images.json
文件,包含了刚才抓取的所有内容。
如果需要以每个 Item
输入一行 Json
,输出后缀为 jl
,为 jsonline
的缩写:
1 | scrapy crawl images -o images.jl |
或者
1 | scrapy crawl images -o images.jsonlines |
除了以上格式,还可以自定义其他输出,如 csv
, xml
, pickle
, marshal
和 ftp
远程输出:
1 | scrapy crawl images -o images.csv |
其中,ftp
输出需正确配置用户名、密码、地址、书城路径。
使用 Item Pipeline
如果需要实现更复杂的操作,如将结果保存到数据库,筛选 item
等需要定义 Item Pipeline
来实现。
Item Pipeline
为项目管道,当 Item
生成后,它会自动被送到 Item Pipeline
进行处理,我们常用 Item Pipeline
来实现下面操作:
- 清理
HTML
数据 - 验证爬取数据,检查爬取字段
- 查重并丢弃重复内容
- 将爬取结果保存到数据库
要实现 Item Pipeline
,只需定义一个类并实现 process_item()
方法即可。启用 Item Pipeline
后,Item Pipeline
会自动调用这个方法。process_item()
方法必须返回包含数据的字典或 Item
对象,或者抛出 DropItem
异常。
实现如下:
1 | import pymongo |
定义好 MongoPipeline
之后,还需要在 settings.py
中使用它,需要定义 MongoDB
连接常量。
1 | ITEM_PIPELINES = { |
在重新执行爬取,命令如下:
1 | scrapy crawl images |
爬取完成之后, MongoDB
中就创建了一个 gakki_images
数据库和 gakki_images
集合。
一些自定义
仔细分析豆瓣的 URL
我们会发现,以 https://img3.doubanio.com/view/photo/r/public/p747045173.jpg
为例,网站是通过字符串下标的第 37
位控制加载图片的大小的,豆瓣服务器的图片存储分为 m
, l
, r
等不同的格式,而 r
对应的就是 row
,是存储的最高清的图片格式。
我们在所有图片页进行爬取,抓到的是 m
缩略图格式,现在需要将 URL
进行修改,获取原生的图片。
定义一个新的图片处理管道:
1 | class SplitPipeline(object): |
同样,在 settings.py
中定义使用:
1 | ITEM_PIPELINES = { |
再次执行爬取,就可以得到 row
豆瓣最高清格式的图片。