0x01 Scrapy 简介
Scrapy
是Python
开发的一个快速、高层次的屏幕抓取和web
抓取框架,用于抓取web
站点并从页面中提取结构化的数据。Scrapy
用途广泛,可以用于数据挖掘、监测和自动化测试。
Scrapy
吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider
、sitemap
爬虫等,最新版本又提供了web2.0
爬虫的支持。
0x02 Scrapy 架构
Scrapy Engine(引擎): 负责Spider
、ItemPipeline
、Downloader
、Scheduler
中间的通讯,信号、数据传递等。
Scheduler(调度器): 它负责接受引擎发送过来的Request
请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
Downloader(下载器): 负责下载Scrapy Engine
(引擎)发送的所有Requests
请求,并将其获取到的Responses
交还给Scrapy Engine
(引擎),由引擎交给Spider
来处理。
Spider(爬虫): 它负责处理所有Responses
,从中分析提取数据,获取Item
字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler
(调度器)。
Item Pipeline(管道): 它负责处理Spider
中获取到的Item
,并进行进行后期处理(详细分析、过滤、存储等)的地方。
Downloader Middlewares(下载中间件): 一个可以自定义扩展下载功能的组件。
Spider Middlewares(Spider中间件): 一个可以自定扩展和操作引擎和Spider
中间通信的功能组件
0x03 Scrapy 原理
引擎从调度器中取出一个链接(URL
)用于接下来的抓取。
引擎把URL
封装成一个请求(Request
)传给下载器。
下载器把资源下载下来,并封装成应答包(Response
)
爬虫解析Response
解析出实体(Item
),则交给实体管道进行进一步处理
解析出的是衔接(URL
),则把URL
交给调度器等待抓取
0x04 Scrapy 安装
Windows平台:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1. pip3 install wheel 2. 安装Twisted 进入http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 根据自身系统环境选择下载。 比如我的win10 64位 python3.7 则下载Twisted‑19.10.0‑cp37‑cp37m‑win_amd64.whl 进入本地下载后的文件夹 pip3 install Twisted‑19.10.0‑cp37‑cp37m‑win_amd64.whl 3. pip3 install lxml 4. pip3 install pyopenssl 5. pip3 install pypiwin32 6. pip3 install scrapy
Linux平台:
pip加速:
在国内pip
下载速度特别慢,经常会下载失败,建议更改国内源下载。查看文章 –> Python pip配置国内源
命令行工具 成功安装好Scrapy
后,在命令行里输入scrapy -h
即可查看帮助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 scrapy -h scrapy <command > -h Global commands: startproject genspider settings runspider shell fetch view version Project-only commands: crawl check list edit parse bench
0x05 Scrapy 实战案例(单页面)
新建项目 :新建一个新的爬虫项目
明确目标 :明确你想要抓取的目标
制作爬虫 :制作爬虫开始爬取网页
存储内容 :设计管道存储爬取内容
步骤一 新建爬虫项目 新建一个名为 mySpider
的爬虫项目
1 scrapy startproject mySpider
输入完命令后,在当前目录下会出现一个mySpider
的文件夹,目录结构如下
1 2 3 4 5 6 7 8 9 10 mySpider/ scrapy.cfg mySpider/ __init__.py items.py pipelines.py settings.py spiders/ __init__.py ...
文件说明:
scrapy.cfg
项目的配置信息。主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)
mySpider/
项目的Python模块,将会从这里引用代码。
mySpider/items.py
项目的目标文件。设置数据存储模板,用于结构化数据。
mySpider/pipelines.py
项目的管道文件。数据处理行为,如:一般结构化的数据持久化
mySpider/settings.py
项目的配置文件,如:递归的层数、并发数,延迟下载等
mySpider/spiders/
爬虫代码目录,如:创建文件,编写爬虫规则
步骤二 明确抓取目标
明确目标:爬取http://www.itcast.cn/channel/teacher.shtml
页面中所有讲师的姓名、职称和个人信息。
打开编写 mySpider
目录下的 items.py
。
自定义 姓名name
、职称title
和 个人信息info
等字段
1 2 3 4 5 6 7 import scrapyclass MyspiderItem (scrapy.Item): name = scrapy.Field() title = scrapy.Field() info = scrapy.Field()
步骤三 制作爬虫代码 在 mySpider/
目录下输入命令,将会在 mySpider/spider
目录下自动生成一个名为 itcast.py
的爬虫文件,并指定爬取域的范围为 itcast.cn
,注意这里的爬虫名不能与项目名称起一样的。
1 scrapy genspider itcast "itcast.cn"
打开 mySpider/spider
目录里的 itcast.py
,自动生成了下列代码:
1 2 3 4 5 6 7 8 9 import scrapyclass ItcastSpider (scrapy.Spider): name = "itcast" allowed_domains = ["itcast.cn" ] start_urls = ['http://www.itcast.cn/' ] def parse (self, response ): pass
参数介绍:
name = ""
:这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
allow_domains = []
是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
start_urls = ()
:爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
parse(self, response)
:负责解析返回的网页数据(response.body),提取结构化数据(生成item),生成需要下一页的URL请求。
导入刚才编写的items.py
文件
1 from mySpider.items import MyspiderItem
将start_urls
的值修改为需要爬取的初始url
1 start_urls = ["http://www.itcast.cn/channel/teacher.shtml" ]
修改parse()
方法,使用XPath
语法对返回的response
网页数据进行匹配提取
1 2 3 4 5 6 7 8 9 10 11 12 13 def parse (self, response ): items = [] for each in response.xpath("//div[@class='li_txt']" ): item = MyspiderItem() name = each.xpath("h3/text()" ).extract() title = each.xpath("h4/text()" ).extract() info = each.xpath("p/text()" ).extract() item['name' ] = name[0 ] item['title' ] = title[0 ] item['info' ] = info[0 ] items.append(item) return items
Scrapy
支持正则语法、CSS
语法和XPath
语法对网页内容进行匹配,这里推荐使用XPath
语法,Scrapy官网也是默认支持使用XPath
语法。
以上parse()
方法用到了XPath
语法对指定内容进行匹配,如果有学过CSS
,那么这里就非常容易理解XPath
语法了。
下面将简单对XPath
语法举例介绍,更多详细点击查看XPath教程
首先分析源代码,找到我们所需要的内容
先介绍个简单的方法,可以直接在火狐或者谷歌浏览器中,找到相应的位置,鼠标右键->复制->XPath
可以直接生成XPath
语句。
复制生成的代码如下
1 /html/body/div[1]/div[5]/div/div[2]/div[13]/ul/li[1]/div[2]
这样方法生成的XPath
语句非常的长,而且不容易理解。
接下来手动构造的XPath
语句进行内容匹配
以上图源码为例
1 2 3 4 5 <div class ="li_txt" > <h3 > 于老师</h3 > <h4 > 高级讲师</h4 > <p > Java企业级应用专家、WEB技术专家,中科院软件工程硕士。07年起曾主持研发过多套软件培训课程与教材,精通JAVAEE、PHP、RUBY、JavaSCRIPT、RIA等多种主流开发语言,曾主持参与过中国联通UMMS二期工程等多个大项目。</p > </div >
现在要匹配class
为li_txt
的div
,构造XPath
语句为
进一步匹配该div
下的h3
标签的内容,构造XPath
语句为
1 //div[@class='li_txt'][1]/h3/text()
从以上两个匹配例子可以很清楚的知道我们所要匹配的是什么,其中//
表示任意的,后接div
表示当前页面所有的div
,而div
后接的[@class='li_txt']
表示指定class
为li_txt
的div
,/
表示该div
紧接下一级内的子标签h3
,text()
表示当前h3
中的文本内容
到目前为止,已经成功使用了XPath
语法,但是难免保证匹配到的都是正确的结果,所以接下来我们需要验证下我们的XPath
是否正确
一种办法是使用谷歌浏览器的插件:XPath Helper
。需要从谷歌网上应用店下载,如果没有梯子的话访问不了,这里推荐一个免费的梯子,有需要的可以下载佛跳墙 ,连接上梯子网络后,即可访问谷歌应用商店 搜索下载插件
安装完插件后,点击右上角的X
图标即可启动,然后在左边输入框中输入XPath
语句,右边实时显示匹配的结果,清晰明了。
但此时Scrapy
通过response.xpath
(语法)不是直接获取字符串,我们需要将其转为字符串格式,通过以下方式可以得到字符串
1 2 3 4 response.xpath().get() response.xpath().getall() response.xpath()..extract()[0 ] response.xpath()..extract_first()
另一种方法是通过Scrapy
自带的交互式shell
来准确验证XPath
语句,具体在命令行操作如下,如果能正确print()
输出结果,则表示XPath
正确。
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 zmj@ubuntu:~/桌面$ scrapy shell http://www.itcast.cn/channel/teacher.shtml 2020-03-21 07:04:51 [scrapy.utils.log] INFO: Scrapy 2.0.0 started (bot: scrapybot) ...... 2020-03-21 07:04:51 [scrapy.core.engine] INFO: Spider opened 2020-03-21 07:04:51 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.itcast.cn/channel/teacher.shtml> (referer: None) [s] Available Scrapy objects: [s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc) [s] crawler <scrapy.crawler.Crawler object at 0x7f963e8140d0> [s] item {} [s] request <GET http://www.itcast.cn/channel/teacher.shtml> [s] response <200 http://www.itcast.cn/channel/teacher.shtml> [s] settings <scrapy.settings.Settings object at 0x7f963e80ff10> [s] spider <DefaultSpider 'default' at 0x7f963e369210> [s] Useful shortcuts: [s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed) [s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help ) [s] view(response) View response in a browser In [1]: res = response.xpath("//div[@class='li_txt']/h3/text()" ) In [2]: print (res) [<Selector xpath="//div[@class='li_txt']/h3/text()" data='王老师' >, <Selector xpath="//div[@class='li_txt']/h3/text()" data='孙老师' >, <Selector xpath="//div[@class='li_txt']/h3/text()" data='李老师' >, ...]
最后总结 mySpider/spider/itcast.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 import scrapyfrom mySpider.items import MyspiderItemclass ItcastSpider (scrapy.Spider): name = 'itcast' allowed_domains = ['itcast.cn' ] start_urls = ['http://www.itcast.cn/channel/teacher.shtml' ] def parse (self, response ): items = [] for each in response.xpath("//div[@class='li_txt']" ): item = MyspiderItem() name = each.xpath("h3/text()" ).extract() title = each.xpath("h4/text()" ).extract() info = each.xpath("p/text()" ).extract() item['name' ] = name[0 ] item['title' ] = title[0 ] item['info' ] = info[0 ] items.append(item) return items
步骤四 存储爬取内容 首先编辑 mySpider/
目录下的 settings.py
文件,找到如下代码去除注释。这么做的目的是为了防止某些网站使用了反爬虫策略,进行绕过。
1 2 3 4 5 6 7 8 9 10 11 ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' , 'Accept-Language' : 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0' , } ITEM_PIPELINES = { 'mySpider.pipelines.MyspiderPipeline' : 300 , }
上面的 DEFAULT_REQUEST_HEADERS
中的为请求头中的数据,我们可以在浏览器中->检查元素->网络->刷新加载->查看请求头 中获取数据
Scrapy
保存信息的最简单的方法主要有四种,-o
输出指定格式的文件,命令如下:
1 scrapy crawl itcast -o teachers.json
json lines
格式,默认为Unicode
编码
1 scrapy crawl itcast -o teachers.jsonl
xml
格式
1 scrapy crawl itcast -o teachers.xml
csv
逗号表达式,可用Excel
打开
1 scrapy crawl itcast -o teachers.csv
注意:当保存成json
格式时,并不会直接生成中文,还是一长串字符格式,具体效果如下
为了解决这个问题,我们可以自定义数据保存的格式
编辑管道文件 mySpider/pipelines.py
,默认增加的代码如下
1 2 3 4 5 class MyspiderPipeline (object ): def process_item (self, item, spider ): return item
将其修改为如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import jsonclass MyspiderPipeline (object ): def __init__ (self ): self .filename = open ("teacher.json" ,"w" ) def process_item (self, item, spider ): text = json.dumps(dict (item),ensure_ascii = False ) + "\n" self .filename.write(str (text)) return item def close_spider (self,spider ): self .filename.close()
然后回到mySpider/
目录下,执行运行命令
此时成功将存储的json
数据变为中文格式
0x06 Scrapy 实战案例(分页面)
在上一个案例中,介绍了如何爬取单页面的内容,接下来介绍下如何在分页模式进行爬取多页面内容。本次案例目标网站页面如下
步骤一 新建爬虫项目 新建一个名为 itheimaSpider
的爬虫项目,建议项目名字均以 网站域名+Spider
格式命名。
1 scrapy startproject itheimaSpider
步骤二 明确抓取目标
明确目标:爬取http://yun.itheima.com/jishu/index/p/1.html
所有分页中的文章标题、介绍、链接、标签、浏览数和日期。
打开编写 itheimaSpider/
目录下的 items.py
。
1 2 3 4 5 6 7 8 9 10 import scrapyclass ItheimaspiderItem (scrapy.Item): itheima_title = scrapy.Field() itheima_introduce = scrapy.Field() itheima_url = scrapy.Field() itheima_tag = scrapy.Field() itheima_view = scrapy.Field() itheima_time = scrapy.Field()
步骤三 制作爬虫代码 在 itheimaSpider/
目录下输入命令,自动生成 itheima
爬虫文件,注意爬虫文件名不要与项目名一样
1 scrapy genspider itheima "itheima.com"
编辑 itheimaSpider/spider/itheima.py
文件
从源码分析一下页面跳转链接之间的url
的区别
从这几个链接很容易可以看出,页面之间的跳转链接是由yun.itheima.com
和各a
标签中的href
属性拼接而成的
提取出url
相同的部分
1 url = "http://yun.itheima.com"
修改置start_urls
值
1 start_urls = ['http://yun.itheima.com/jishu/' ]
导入items
模块
1 from itheimaSpider.items import ItheimaspiderItem
XPath
语法匹配指定内容
1 2 3 4 5 6 7 8 //div[@class ='fl' ]/ul/li/a/h2/text() //div[@class ='fl' ]/ul/li/a/p/text() //div[@class ='fl' ]/ul/li/a/@href //div[@class ='fl' ]/ul/li/a/div/h3/text() //div[@class ='fl' ]/ul/li/a/div/p[1 ]/text() //div[@class ='fl' ]/ul/li/a/div/p[2 ]/text() //div[@class ='pagebox' ]/div/a[@class ='next' ]/@href
提取匹配出每篇文章各自内容的XPath
语句部分,作为循环主体
1 //div[@class ='fl' ]/ul/li
然后再子循环读取每篇文章下各自的内容
1 2 3 4 5 6 7 8 9 10 11 12 for line in response.xpath("//div[@class='fl']/ul/li" ): item = ItheimaspiderItem() item['itheima_title' ] = line.xpath("./a/h2/text()" ).get() item['itheima_introduce' ] = line.xpath("./a/p/text()" ).get() item['itheima_url' ] = self .url + line.xpath("./a/@href" ).get() item['itheima_tag' ] = line.xpath("./a/div/h3/text()" ).get() item['itheima_view' ] = line.xpath("./a/div/p[1]/text()" ).get() item['itheima_time' ] = line.xpath("./a/div/p[2]/text()" ).get() yield item
为了能够获取所有分页面的内容,需要自动获取下一页的跳转url
,这里使用XPath
匹配到下一页a
标签的href
属性,然后拼接上http://yun.itheima.com
即可。
1 2 3 4 5 6 7 next_page = response.xpath("//div[@class='pagebox']/div/a[@class='next']/@href" ).get() if next_page: next_url = response.urljoin(next_page) yield scrapy.Request(next_url, callback = self .parse)
最后总结下 itheimaSpider/spiders/itheima.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 import scrapyfrom itheimaSpider.items import ItheimaspiderItemclass ItheimaSpider (scrapy.Spider): name = 'itheima' allowed_domains = ['itheima.com' ] start_urls = ['http://yun.itheima.com/jishu/' ] url = "http://yun.itheima.com" def parse (self, response ): for line in response.xpath("//div[@class='fl']/ul/li" ): item = ItheimaspiderItem() item['itheima_title' ] = line.xpath("./a/h2/text()" ).get() item['itheima_introduce' ] = line.xpath("./a/p/text()" ).get() item['itheima_url' ] = url + line.xpath("./a/@href" ).get() item['itheima_tag' ] = line.xpath("./a/div/h3/text()" ).get() item['itheima_view' ] = line.xpath("./a/div/p[1]/text()" ).get() item['itheima_time' ] = line.xpath("./a/div/p[2]/text()" ).get() yield item next_page = response.xpath("//div[@class='pagebox']/div/a[@class='next']/@href" ).get() if next_page: next_url = response.urljoin(next_page) yield scrapy.Request(next_url, callback = self .parse)
步骤四 存储爬取内容 编辑 itheimaSpider/
目录下的 settings.py
文件,找到如下代码去除注释并修改代码。
1 2 3 4 5 6 7 8 9 10 ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' } ITEM_PIPELINES = { 'itheimaSpider.pipelines.ItheimaspiderPipeline' : 300 , }
最后在 itheimaSpider/
输入运行爬虫命令
1 scrapy crawl itheima -o itheima.csv
保存数据在 itheima.csv
文件中
0x07 Scrapy 爬虫进阶(多级页面)
上个案例介绍了如何在多个分页中爬取到我们想要的数据,接下来这个案例将介绍如何在多级页面中获取到我们想要的数据。
本次演示网站为https://www.ivsky.com/bizhi/
,目标是爬取该网页中所有图集中的图片
本次为三级页面示例,通过一级壁纸首页,找到二级图集首页,再通过图集找到三级图片页面地址,最终目的是要爬取下载所有图集中的图片。
1 2 3 一级:https://www.ivsky.com/bizhi/ 二级:https://www.ivsky.com/bizhi/hudie_v58539/ 三级:https://www.ivsky.com/bizhi/hudie_v58539/pic_921157.html
步骤一 新建爬虫项目 新建一个名为 ivskySpider
的爬虫项目
1 scrapy startproject ivskySpider
步骤二 明确抓取目标
明确目标:爬取https://www.ivsky.com/bizhi/
所有分页中的图片。
打开编写 ivskySpider/
目录下的 items.py
。
1 2 3 4 5 6 7 8 import scrapyclass ItheimaspiderItem (scrapy.Item): image_urls = scrapy.Field() images = scrapy.Field()
步骤三 制作爬虫代码 在 ivskySpider/
目录下输入命令,自动生成 ivsky
爬虫文件,-t crawl
表示使用crawl
的模板。
1 scrapy genspider -t crawl ivsky "ivsky.com"
打开 ivskySpider/spider/ivsky.py
文件,默认增加的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Ruleclass IvskySpider (CrawlSpider ): name = 'ivsky' allowed_domains = ['ivsky.com' ] start_urls = ['http://ivsky.com/' ] rules = ( Rule(LinkExtractor(allow=r'Items/' ), callback='parse_item' , follow=True ), ) def parse_item (self, response ): item = {} return item
导入 items.py
文件
1 from ivskySpider.items import IvskyspiderItem
修改 start_urls
值如下
1 start_urls = ['https://www.ivsky.com/bizhi/' ]
先分析一下总共需要获取的url
:首页翻页url
,图集翻页url
,以及图片链接url
的格式,进行正则匹配获取,然后编写rules
规则匹配
1 2 3 4 5 6 7 8 9 10 11 分析获取url逻辑顺序: 首页 url:www.ivsky.com/bizhi/ 首页翻页:不回调 url:https://www.ivsky.com/bizhi/index_d+.html 图集首页:不回调 url:https://www.ivsky.com/bizhi/\w+_v\d+/ 图集翻页-》需回调:提取图片url,供下载使用 url:https://www.ivsky.com/bizhi/\w+_v\d+/pic_d+.html 写rule规则时与逻辑顺序相反: rule图集翻页-》需回调:提取图片url,供下载使用 url:https://www.ivsky.com/bizhi/\w+_v\d+/pic_d+.html rule图集首页:不回调 url:https://www.ivsky.com/bizhi/\w+_v\d+/ rule首页翻页:不回调 url:https://www.ivsky.com/bizhi/index_d+.html 首页 url:www.ivsky.com/bizhi/
修改 rules
规则如下,其中 allow
值中的 \d+
代表匹配1次或多任意数字值, \w+
代表匹配1次或多次任意字符值,然后会自动匹配当前页面中所有符合该规则的url
格式,更多规则点击查看正则匹配
1 2 3 4 5 rules = ( Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/\w+_v\d+/pic_d+.html' ), callback='parse_item' , follow=True ), Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/\w+_v\d+/' ), follow=True ), Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/index_d+.html' ), follow=True ), )
然后XPath
匹配图片下载的链接
1 2 3 //div[@id ='pic_con' ]/div/img/@src response.xpath("//div[@id='pic_con']/div/img/@src" ).get()
注意:笔者这里XPath
遇到个坑,之前因为过分相信XPath Helper
的验证机制,导致在这踩到坑了。有时候XPath Helper
在谷歌浏览器中能正确匹配出想要的内容,但是到了Scrapy
中却无法匹配到正确结果。这里强调如果遇到这种情况,建议手动在Scrapy shell
中重新使用的XPath
语法进行匹配,最后以此为正确结果。具体的情况请看本文结尾的小贴士
分析。
修改 parse_item()
方法如下,主要是将获取的图片url
进行list
数组返回
1 2 3 4 5 6 7 def parse_item (self, response ): item = IvskyspiderItem() image_url = "http:" + response.xpath("//div[@id='pic_con']/div/img/@src" ).get() image_list = [] image_list.append(image_url) item['image_urls' ] = image_list yield item
最后总结 ivskySpider/spider/ivsky.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 scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom ivskySpider.items import IvskyspiderItemfrom scrapy.pipelines.images import ImagesPipelineclass IvskySpider (CrawlSpider ): name = 'ivsky' allowed_domains = ['ivsky.com' ] start_urls = ['https://www.ivsky.com/bizhi/' ] rules = ( Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/\w+_v\d+/pic_\d+.html' ), callback='parse_item' , follow=True ), Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/\w+_v\d+/' ), follow=True ), Rule(LinkExtractor(allow=r'https://www.ivsky.com/bizhi/index_\d+.html' ), follow=True ), ) def parse_item (self, response ): item = IvskyspiderItem() image_url = "http:" + response.xpath("//div[@id='pic_con']/div/img/@src" ).get() image_list = [] image_list.append(image_url) item['image_urls' ] = image_list yield item
步骤四 存储爬取内容 编辑 ivskySpider/
目录下的 settings.py
文件,找到如下代码去除注释并修改代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' , 'Referer' : 'https://www.ivsky.com' , } ITEM_PIPELINES = { 'scrapy.pipelines.images.ImagesPipeline' : 300 , } IMAGES_STORE = 'img'
最后在 ivskySpider/
输入运行爬虫命令
保存数据在 ivskySpider/img/full
目录下
小贴士
这里着重提示真正的XPath
语句以Scrapy shell
中的语句为准,在其他的XPath
插件中的语句仅供参考。
例如下面这种情况:两个XPath
语句目的是为了匹配出图片的链接地址
首先是这句XPath
,使用谷歌的XPath Helper
插件匹配是正确的,没有问题
1 //a[@class ='page-next' ]/img/@src
但是同样的这句话,到了Scrapy shell
中却失效了,没有匹配到任何信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 zmj@ubuntu:~/桌面$ scrapy shell https://www.ivsky.com/bizhi/hudie_v58539/pic_921157.html ...... 2020-03-21 05:10:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.ivsky.com/bizhi/hudie_v58539/pic_921157.html> (referer: None) [s] Available Scrapy objects: [s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc) [s] crawler <scrapy.crawler.Crawler object at 0x7fead038d610> [s] item {} [s] request <GET https://www.ivsky.com/bizhi/hudie_v58539/pic_921157.html> [s] response <200 https://www.ivsky.com/bizhi/hudie_v58539/pic_921157.html> [s] settings <scrapy.settings.Settings object at 0x7fead038d210> [s] spider <DefaultSpider 'default' at 0x7feacfee26d0> [s] Useful shortcuts: [s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed) [s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help ) [s] view(response) View response in a browser In [1]: url = response.xpath("//a[@class='page-next']/img/@src" ).get() In [2]: print (url) None In [3]:
既然scrapy
不认这句XPath
,那么只好在scrapy shell
重新手动匹配
1 2 3 4 5 6 7 8 9 10 11 In [3]: url = response.xpath("//div[@id='pic_con']" ).get() In [4]: print (url) <div id ="pic_con" ><div><script>dy("pic_tonext" );</script><img id ="imgis" src="//img.ivsky.com/img/bizhi/pre/201910/07/hudie.jpg" alt="美丽可爱的蝴蝶图片" ></div></div> In [5]: url = response.xpath("//div[@id='pic_con']/div/img/@src" ).get() In [6]: print (url) //img.ivsky.com/img/bizhi/pre/201910/07/hudie.jpg In [7]:
最后匹配到的真正url
的XPath
语句为
1 2 3 //div[@id ='pic_con' ]/div/img/@src response.xpath("//div[@id='pic_con']/div/img/@src" ).get()
同样的再将scrapy shell
匹配出的这句XPath
放到谷歌浏览器中也没法匹配到url
造成这个原因是使用Scrapy
在爬取目标网站时与在浏览器访问目标网站时的源码有些差距。
参考文章