网站建设分销协议,wordpress pin,win7 建设网站服务器,国内哪家网站做的系统纯净scrapy基本介绍 Scrapy一个开源和协作的框架#xff0c;其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的#xff0c;使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。 但目前Scrapy的用途十分广泛#xff0c;可用于如数据挖掘、监测和自动化测试等领域…scrapy基本介绍 Scrapy一个开源和协作的框架其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。 但目前Scrapy的用途十分广泛可用于如数据挖掘、监测和自动化测试等领域也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy 是基于twisted框架开发而来twisted是一个流行的事件驱动的python网络框架。 因此Scrapy使用了一种非阻塞又名异步的代码来实现并发。 Scrapy架构 百度上找到的Scrapy架构图 1、引擎(Engine) 引擎负责控制系统所有组件之间的数据流并在某些动作发生时触发事件。 有关详细信息请参见上面的数据流部分。 ------ 2、调度器(Scheduler) 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址 ------ 3、下载器(Dowloader) 用于下载网页内容, 并将网页内容返回给Engine下载器是建立在twisted这个高效的异步模型上的 ------ 4、爬虫(Spiders) SPIDERS是开发人员自定义的类用来解析responses并且提取items或者发送新的请求 ------ 5、项目管道(Item Pipelines) 在items被提取后负责处理它们主要包括清理、验证、持久化比如存到数据库等操作 下载器中间件(Downloader Middlewares)位于Scrapy引擎和下载器之间主要用来处理从Engine传到Downloader的请求request已经从Downloader传到Engine的响应response。 ------ 6、爬虫中间件(Spider Middlewares) 位于Engine和SPIDERS之间主要工作是处理Spiders的输入即responses和输出即requests Scrapy安装 windows安装命令pip3 install scrapy 依赖项安装 pip3 install lxml pip3 install whee pip3 install pyopenssl 依赖项如果已经安装的可以跳过 官网链接ghttps://docs.scrapy.org/en/latest/topics/commands.htmlscrapy官网链接ghttps://docs.scrapy.org/en/latest/topics/commands.html scrapy框架使用及命令详解
常用命令
查看帮助 scrapy -hscrapy command -h 有两种命令其中Project-only必须切到项目文件夹下才能执行而Global的命令则不需要 Global commands
startproject #创建项目
genspider #创建爬虫程序
settings #如果是在项目目录下则得到的是该项目的配置
runspider #运行一个独立的python文件不必创建项目
shell #scrapy shell url地址 在交互式调试如选择器规则正确与否
fetch #独立于程单纯地爬取一个页面可以拿到请求头
view #下载完毕后直接弹出浏览器以此可以分辨出哪些数据是ajax请求
version #scrapy version 查看scrapy的版本scrapy version -v查看scrapy依赖库的版本
Project-only commands
crawl #运行爬虫必须创建项目才行确保配置文件中ROBOTSTXT_OBEY False
check #检测项目中有无语法错误
list #列出项目中所包含的爬虫名
edit #编辑器一般不用
parse #scrapy parse url地址 --callback 回调函数 #以此可以验证我们的回调函数是否正确
bench #scrapy bentch压力测试
对于爬虫而言我们需要关心及常用的命令就三个
startproject创建项目、 genspider创建爬虫程序、crawl启动爬虫
创建项目
手动新建一个“day23”的文件夹进入Teminal终端
scrapy startproject Newspro #Newspro是项目名称
回车执行后就会自动帮我“day23”的文件夹下创建Newspro
ps这里项目名称如果写成News proday23下父级目录会叫pro然后是子级目录News 文件目录 文件说明 ● scrapy.cfg项目的主配置信息用来部署scrapy时使用爬虫相关的配置信息在settings.py文件中。 ● items.py设置数据存储模板用于结构化数据如Django的Model ● pipelines数据处理行为如一般结构化的数据持久化 ● settings.py配置文件如递归的层数、并发数延迟下载等。强调:配置文件的选项必须大写否则视为无效正确写法USER_AGENTxxxx ● spiders爬虫目录如创建文件编写爬虫规则 创建爬虫程序
上一步Teminal终端中创建完项目之后已经提示需要先cd Newspro开始创建爬虫程序
cd Newspro #进入项目文件夹
scrapy genspider wangyi news.163.com/ #创建爬虫程序1
即告诉Teminal终端
我要用scrapy框架创建(genspider一个叫wangyi的爬虫程序news.163.com/是要爬取的url可以省略https://
可以看到在Spiders文件夹下就自动帮我们生成了一个wangyi.py的文件并且文件中自动写好了一个类以及一些配置参数。
当然如果是老手也可以自己手动创建模块但是小白的话更推荐用命令创建不然模块中少写了参数就会导致一些bug……
另外需要注意里面的parse方法parse这个方法名不能改这是框架自带的回调函数 再创建一个环球新闻网的模块也自动生成了一个huanqiu.py的模块
模块中的域名默认是按照http进行拼的如果不对也可以手动改成https scrapy genspider huanqiu huanqiu.com在做爬虫的时候我们可能不止爬取一个网站规范是
将它们全部放在Spiders文件夹下每一个要爬取的网站建立单独一个模块然后在模块里完善具体的爬虫逻辑和解析逻辑。
完整的创建项目 - 创建爬虫程序代码如下
scrapy startproject Newspro #Newspro是项目名称
cd Newspro #进入项目文件夹
scrapy genspider wangyi news.163.com #创建爬虫程序1
scrapy genspider huanqiu huanqiu.com #创建爬虫程序2
Spider类详解 Spiders是定义如何抓取某个站点或一组站点的类包括如何执行爬行即跟随链接以及如何从其页面中提取结构化数据即抓取项目。 换句话说Spiders是您为特定站点或者在某些情况下一组站点爬网和解析页面定义自定义行为的地方。 ① 生成初始的Requests来爬取第一个URLS并且标识一个回调函数 第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发 parse不能改名必须叫parse ------- ② 在回调函数中解析response并且返回值 返回值可以4种 包含解析数据的字典 Item对象 新的Request对象新的Requests也需要指定一个回调函数 或者是可迭代对象包含Items或Request ------- ③ 在回调函数中解析页面内容 通常使用Scrapy自带的Selectors但很明显你也可以使用Beutifulsouplxml或其他你爱用啥用啥。 ------- ④ 最后针对返回的Items对象将会被持久化到数据库 通过Item Pipeline组件存到数据库https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline 或者导出到不同的文件通过Feed exportshttps://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports 启动爬虫程序
获取网易新闻的html信息修改添加wangyi.py模块中的代码
import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址可以放多个#回调函数解析方法def parse(self, response):#windos系统记得写encodingutf8不然写入的会是一个空文件with open(new163..html,w,encodingutf8) as f: f.write(response.text)
启动爬虫程序方式1Teminal在终端输入以下代码
ls
scrapy crawl wangyi #crawl启动的意思后面跟上要启动的模块名回车之后会输出很多日志日志跑完就会出现一个new163的html文件说明执行成功
html文件可以直接用浏览器打开就是网页新闻的页面 如果不想看到这一堆日志可以在启动文件的时候加上--nolog
一般刚开始调试的时候不建议关闭日志否则哪里写错了也看不到报错信息
scrapy crawl wangyi --nolog #启动网易模块且不显示日志
每次都得在终端输入命令还是有点麻烦所以也有另一种启动方式
启动爬虫程序方式2通过run来执行启动
scrapy框架没有自带的这个功能我们可以创建一个py脚本文件
在项目的根目录下进行创建一个py文件例如我的项目文件名叫Newspro就是在它下面创建 运行以下代码也可以进行启动爬虫程序
from scrapy.cmdline import execute#[scrapy, crawl, 文件名]
execute([scrapy, crawl, wangyi])#需要关闭日志的话加上--nolog即可
# execute([scrapy, crawl, wangyi,--nolog])
项目使用--scrapy实战案例详解 基于前面创建的Newspro项目下的wangyi.py模块批量爬取网易新闻首页板块tab的15个新闻分类里的所有新闻标题如下截图圈出的板块 ------------------------------------------------------------------------------------------------------------------------- 当鼠标悬浮在tab标题上时悬浮在不同标题会自动切换显示对应内容说明此时非当前tab标题下的内容被隐藏了浏览器并没有真正对服务器发起访问请求比如说这个15个tab标题分类下每个分类有100条新闻那么总共就是1500条新闻都在第一次的请求响应中。 通过之前爬取下来的html文件对比
通过对比html文件就可以看出响应信息只是放在不同位置 ⑴ 批量爬取网易新闻标题 明确完整的爬取需求 ① tab板块中15个分类的所有新闻分类即tab名称 ② 所有的新闻标题 ③ 以及所有的新闻内容 通过之前爬取下来的首页信息的html文件中可以看出目前能拿到的是tab名称和新闻标题 新闻内容需要请求具体每个新闻的url所以分三步来实现 第一步先来完成首页最容易获取的信息解析新闻标题 第二步解析tab分类名称 第三步最后爬取具体的所有新闻内容请求所有新闻的url进行获取 现在正式开始实现第一步解析新闻标题
解析新闻标题 错误示范
先打开浏览器 - f12进行元素xpath定位先找到新闻标题对应的素
//div[classnews_title]/h3/a/text() import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#回调函数解析方法def parse(self, response):#获取新闻标题的列表scrapy框架里也支持xpath语法news_title_list response.xpath(//div[classnews_title]/h3/a)print(news_title_list::,news_title_list)print(news_title_list长度,len(news_title_list)) #长度即新闻的个数
然后在bin模块的脚本文件中启动(关闭了日志)
bin文件执行完成之后发现wangyi.py文件的输出并没有返回预期的信息 重新开启日志再run一下发现有报错 在上面的网页新闻f12元素中是能够找到新闻标题的说明定义的xapth规则是没问题的
但是为什么解析不出来数据呢这是一个坑极大可能性是在f12中我们看到的数据是js渲染的js把数据渲染到对应的标签里了
所以解决办法是我们要先去看之前爬取下来的html文件找到对应的hidden隐藏的属性值 解析新闻标题 正确示范重新修改代码
import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#回调函数解析方法def parse(self, response):#获取新闻标题的列表scrapy框架里也支持xpath语法news_title_list response.xpath(//div[contains(ne-if,{{)]/div/a/text())print(news_title_list::,news_title_list)print(news_title_list长度,len(news_title_list)) #长度即新闻的个数
重新run一下bin文件这次有返回信息说明解析成功
但是框架自动帮我们用Selector封装了一个data将新闻标题放在data中 我们只想要新闻标题所以使用extract()进行提取直接加在xapth后面就行
import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#回调函数解析方法def parse(self, response):#获取新闻标题的列表scrapy框架里也支持xpath语法news_title_list response.xpath(//div[contains(ne-if,{{)]/div/a/text()).extract()print(news_title_list::,news_title_list)print(news_title_list长度,len(news_title_list)) #长度即新闻的个数重新run一下bin文件这样就提取出了text值即所有的新闻标题 extract()还有一个方法extract_first()值提取第一个 总结一下两个提取方法 extract()提取Selector对象里的所有的data的text属性值 extract_first()提取Selector对象里的data的第一个text属性值即索引为0的text ⑵ 爬取新闻分类标题链接 上面爬取新闻标题时是对这个项目的初探 但是有个问题就是只能获取到所有的新闻标题没有新闻tab分类 所以我们重新转换一下思路 ❶ 先建立每个tab分类名称的关系映射字典 - 在回调函数中写解析内容的代码 这次直接提取网页新闻首页html文件中的分类title也就是ne-if这个属性值 由于有15个分类板块这里先取其中6个分类进行循环15次只返回在字典表中存在的 import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#方式1获取新闻标题的列表scrapy框架里也支持xpath语法# news_title_list response.xpath(//div[contains(ne-if,{{)]/div/a/text()).extract_first()# print(news_title_list::,news_title_list)# print(news_title_list长度,len(news_title_list)) #长度即新闻的个数#方式2获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()print(cate_mun__list:,cate_mun__list)for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_mun self.cate_mun_map.get(cate_mun)print(cate_mun)
run以下bin文件取到了6个分类 ❷ 根据这6个分类紧接着去循环爬取新闻标题和url 这样就能将每个分类下的tab名称和新闻标题及url关联上 就相当于在上面代码中进行循环嵌套 import scrapyclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#方式1获取新闻标题的列表scrapy框架里也支持xpath语法# news_title_list response.xpath(//div[contains(ne-if,{{)]/div/a/text()).extract_first()# print(news_title_list::,news_title_list)# print(news_title_list长度,len(news_title_list)) #长度即新闻的个数#方式2获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()print(cate_mun__list:,cate_mun__list)for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_title self.cate_mun_map.get(cate_mun)print(cate_title)#爬取每一个板块的新闻标题# response.xpath(//*[contains(ne-if,{{__i 0}},{{__i 1}})/div/a)#不能写死i0,1所以用他对应的cate_mun来做循环news_selector_list response.xpath(f//*[contains(ne-if,{cate_mun})]/div/a)for news_selector in news_selector_list:news_title news_selector.xpath(text()).extract_first()news_link news_selector.xpath(href).extract_first()print((news_title,news_link,cate_title))再run一下bin文件就得到了每个新闻分类下所有的新闻标题每个新闻链接 ⑶ 批量爬取新闻内容 根据上面取到的新闻分类、新闻标题、新闻链接 接下来还差最后一步爬取到所有对应分类下的、所有新闻标题里的新闻内容 import scrapy
from scrapy.http import Requestclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()print(cate_mun__list:,cate_mun__list)for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_title self.cate_mun_map.get(cate_mun)print(cate_title)#爬取每一个板块的新闻标题# response.xpath(//*[contains(ne-if,{{__i 0}},{{__i 1}})/div/a)#不能写死i0,1所以用他对应的cate_mun来做循环news_selector_list response.xpath(f//*[contains(ne-if,{cate_mun})]/div/a)for news_selector in news_selector_list:news_title news_selector.xpath(text()).extract_first()news_link news_selector.xpath(href).extract_first()print((news_title,news_link,cate_title))#根据源码照猫画虎只要yield Request请求就会自动帮我们将请求压缩到Scheduler、Dowloader参数为url,去重,回调解析函数这里的url是双层for循环下的6个新闻tab分类对应的700多个url回调函数需要自定义用于解析内容;回调函数是下面的parse_news_detail方法注意callback self的时候不要加括号yield Request(urlnews_link,dont_filterTrue,callbackself.parse_news_detail)#parse_news_detail方法相当于是框架自动来完成700多次调用和响应def parse_news_detail(self, response):print(response::,response)#针对返回的response信息解析#提取content的text内容content_list response.xpath(//*[idcontent]/div[classpost_body]/p/text()).extract()#拼接内容content .join(content_list)print(content:,content)run以下bin文件但是content并没有被解析出来 原因是爬的过程网易需要ua头使用框架的好处就是不再需要我们自己添加ua头
只需要在settings.py这个配置文件中将17行放开代码执行的过程中框架就会自动拿到ua帮我放进去
另外21行是是否遵循机器人协议默认为True必要的时候可以改为False不遵循但不建议改 然后重新run以下bin文件执行代码还有两个问题
1、新闻分类的tab名称、和新闻标题及内容是分开的
2、新闻的content为空tab名称、和新闻标题及内容 先来解决第一个问题tab名称、和新闻标题及内容把放在一起
在yield里面加上一个框架自带的meta字典它作用是随着yield Request请求的发送将各自的meta写进各自的response里面这样就简单高效完成了放在一起的关联
import scrapy
from scrapy.http import Requestclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_title self.cate_mun_map.get(cate_mun)#爬取每一个板块的新闻标题# response.xpath(//*[contains(ne-if,{{__i 0}},{{__i 1}})/div/a)#不能写死i0,1所以用他对应的cate_mun来做循环news_selector_list response.xpath(f//*[contains(ne-if,{cate_mun})]/div/a)for news_selector in news_selector_list:news_title news_selector.xpath(text()).extract_first()news_link news_selector.xpath(href).extract_first()#根据源码照猫画虎只要yield Request请求就会自动帮我们将请求压缩到Scheduler、Dowloader参数为url,去重,回调解析函数这里的url是双层for循环下的6个新闻tab分类对应的700多个url回调函数需要自定义用于解析内容;回调函数是下面的parse_news_detail方法注意callback self的时候不要加括号yield Request(urlnews_link,dont_filterTrue,callbackself.parse_news_detail,meta{news_title:news_title,cate_title:cate_title})#parse_news_detail方法相当于是框架自动来完成700多次调用和响应def parse_news_detail(self, response):news_title response.meta.get(news_title)cate_title response.meta.get(cate_title)#针对返回的response信息解析#提取content的text内容content_list response.xpath(//*[idcontent]/div[classpost_body]/p/text()).extract()#拼接内容content .join(content_list)print(cate_title,news_title,content)
Item及 PipeLine
Item(项目) 抓取的主要目标是从非结构化源通常是网页中提取结构化数据。 Scrapy蜘蛛可以像Python一样返回提取的数据。 虽然方便和熟悉但很容易在字段名称中输入拼写错误或返回不一致的数据尤其是在具有许多蜘蛛的较大项目中。 为了定义通用输出数据格式Scrapy提供了Item类。 Item对象是用于收集抓取数据的简单容器。 它们提供类似字典的 API并具有用于声明其可用字段的方便语法。 简单来说 上面的实战案例中我们初步完成了数据的爬取 接下来就是要进行数据清洗、和持久化存储到数据库 在Newspro的项目文件夹下已经自动生成了一个Item.py的文件 在Scrapy中数据模型使用Item类来定义。在这个文件中定义了一个名为NewsproItem的Item类它继承自scrapy.Item类。 # define the fields for your item here like:
# name scrapy.Field() 上面这两句话的意思是 在这个Item类中可以定义需要抓取的数据的字段。 每个字段都可以使用scrapy.Field()来定义以便在抓取过程中存储相应的数据 上面(3) 批量爬取新闻内容最后的代码中print(cate_title,news_title,content)我们已经构建出来三个数据由于没有一个统一的类来管理它们所以输出打印结果看起来就很乱
所以我们在Item.py文件中使用scrapy.item来定义字段、管理它们
import scrapyclass NewsItem(scrapy.Item):title scrapy.Field()cata scrapy.Field()content scrapy.Field()
然后去wangyi.py文件中进行封装item
import scrapy
from scrapy.http import Request
from NewsPro.items import NewsItem #记得导入tiemNewsPro是项目根目录class WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_title self.cate_mun_map.get(cate_mun)#爬取每一个板块的新闻标题# response.xpath(//*[contains(ne-if,{{__i 0}},{{__i 1}})/div/a)#不能写死i0,1所以用他对应的cate_mun来做循环news_selector_list response.xpath(f//*[contains(ne-if,{cate_mun})]/div/a)for news_selector in news_selector_list:news_title news_selector.xpath(text()).extract_first()news_link news_selector.xpath(href).extract_first()#根据源码照猫画虎只要yield Request请求就会自动帮我们将请求压缩到Scheduler、Dowloader参数为url,去重,回调解析函数这里的url是双层for循环下的6个新闻tab分类对应的700多个url回调函数需要自定义用于解析内容;回调函数是下面的parse_news_detail方法注意callback self的时候不要加括号yield Request(urlnews_link,dont_filterTrue,callbackself.parse_news_detail,meta{news_title:news_title,cate_title:cate_title})#parse_news_detail方法相当于是框架自动来完成700多次调用和响应def parse_news_detail(self, response):news_title response.meta.get(news_title)cate_title response.meta.get(cate_title)#针对返回的response信息解析#提取content的text内容content_list response.xpath(//*[idcontent]/div[classpost_body]/p/text()).extract()#拼接内容content .join(content_list)print(cate_title,news_title,content)实例化封装item对象目的1、统一数据目的2、方便item调度 newItem NewsItem()NewsItem[title] news_titleNewsItem[cate] cate_titleNewsItem[content] contentyield newItem 结合最上面的scrapy架构图来说说为什么要封装item 只要yield的是item对象引擎就会把item交给pipelines做数据清洗和存储(对应架构图的7到8) 如果yield的是Request对象相当于是压到了Scheduler(第三步)重新请求Engine(第六步)重新响应解析 ----------------- 代码到这里由于前面数据都已经准备好了所以不需要在重新发起请求直接return item即构建了700多个新闻对应的item对象 所以总的来说解析就两步 没解析完成就yield Request 解析完成拿到数据就yield item对象 PipeLine 在一个项目被scpay抓取之后它被发送到项目管道该项目管道通过顺序执行的几个组件处理它。 每个项目管道组件有时简称为“项目管道”是一个实现简单方法的Python类。他们收到一个项目并对其执行操作同时决定该项目是否应该继续通过管道或被丢弃并且不再处理。 项目管道的典型用途是 - cleansing HTML data清理HTML数据 - validating scraped data (checking that the items contain certain fields)验证爬取的数据 - checking for duplicates (and dropping them)检查重复数据 - storing the scraped item in a database存储爬取到的数据 每个项管道组件都是一个必须实现以下方法的Python类 process_itemself项目蜘蛛 为每个项目管道组件调用此方法process_item() 返回带数据的dict、一个Item 或任何后代类对象返回Twisted Deferred或引发 DropItem异常。丢弃的项目不再由其他管道组件处理。 ------------------------------------------------------------------------------------------------------------------------- 此外他们还可以实现以下方法 open_spiderself蜘蛛打开蜘蛛时会调用此方法 close_spiderself蜘蛛当蜘蛛关闭时调用此方法。 from_crawlerclscrawler 如果存在则调用此类方法以从a创建管道实例Crawler。它必须返回管道的新实例。Crawler对象提供对所有Scrapy核心组件的访问如设置和信号; 它是管道访问它们并将其功能挂钩到Scrapy的一种方式。 上面提到只要yield的是item对象就会由item pipelines来接收
接下来我们进入到项目创建时自动生成的pipelines.py文件中 直接添加打印item 为了确保pipelines.py文件中的process_item方法会工作需要去settings.py文件
将66~68行的代码放开默认是注释掉的让它开启工作 Pipeline的作用
Newspro.pipelines.NewsproPipeline可以构建多个每个Pipeline都可以有不同的功能 比如 写3个Pipeline给每个Pipeline加上权重执行的时候会依次按照从上到下的顺序 第一个Pipeline是把数据存在文件里执行完交给第二个Pipeline 第二个Pipeline是把数据存在mysql里执行完交给第三个Pipeline 第三个Pipeline是把数据存在mongo里 多个Pipeline就是这样以此类推到这里就是scrapy的最后一步闭环动作 然后在wangyi.py文件中提取content的text内容时加上去空格(第60行)
import scrapy
from scrapy.http import Request
from Newspro.items import NewsItemclass WangyiSpider(scrapy.Spider):name wangyi# allowed_domains [news.163.com] #限制爬的域一定要在这个url之下所以先注释start_urls [http://news.163.com/] #爬虫起始地址#建立一个tab分类名称关系映射字典cate_mun_map {{{__i 0}} : 要闻 , {{__i 1}}: 上海,{{__i 3}} : 国内 , {{__i 2}}: 国际,{{__i 4}} : 独家 , {{__i 5}}: 军事,# {{__i 6}} : 财经 , {{__i 7}}: 科技,# {{__i 8}} : 体育 , {{__i 9}}: 娱乐,# {{__i 10}}: 时尚 , {{__i 11}}: 汽车,# {{__i 12}}: 房产 , {{__i 13}}: 航空,# {{__i 14}}: 健康}#回调函数解析方法def parse(self, response):#获取标题和类别cate_mun__list response.xpath(//*[contains(ne-if,{{)]/ne-if).extract()for cate_mun in cate_mun__list:#判断cate_mun是否在字典映射表中只返回有的循环15次只拿符合条件的if cate_mun in self.cate_mun_map:#需要爬取的板块cate_title self.cate_mun_map.get(cate_mun)#爬取每一个板块的新闻标题# response.xpath(//*[contains(ne-if,{{__i 0}},{{__i 1}})/div/a)#不能写死i0,1所以用他对应的cate_mun来做循环news_selector_list response.xpath(f//*[contains(ne-if,{cate_mun})]/div/a)for news_selector in news_selector_list:news_title news_selector.xpath(text()).extract_first()news_link news_selector.xpath(href).extract_first()#根据源码照猫画虎只要yield Request请求就会自动帮我们将请求压缩到Scheduler、Dowloader参数为url,去重,回调解析函数这里的url是双层for循环下的6个新闻tab分类对应的700多个url回调函数需要自定义用于解析内容;回调函数是下面的parse_news_detail方法注意callback self的时候不要加括号yield Request(urlnews_link,dont_filterTrue,callbackself.parse_news_detail,meta{news_title:news_title,cate_title:cate_title})#parse_news_detail方法相当于是框架自动来完成700多次调用和响应def parse_news_detail(self, response):news_title response.meta.get(news_title)cate_title response.meta.get(cate_title)#针对返回的response信息解析#提取content的text内容content_list response.xpath(//*[idcontent]/div[classpost_body]/p/text()).extract()#拼接内容、并去除空格content .join([i.strip() for i in content_list])print(cate_title,news_title,content)实例化封装item对象目的1、统一数据目的2、方便item调度 newItem NewsItem()newItem[title] news_titlenewItem[cate] cate_titlenewItem[content] contentyield newItem重新run一下bin文件
cate(tab分类标题)、title(新闻标题)、content(新闻内容)已经放在一起了
但是仍有一些content内容没有解析到的这是前面遗留的第二个问题(⑶ 批量爬取新闻内容这里)是由于news_selector_list这里xapth定义的解析规则不能适用所有的新闻分类导致的 所以这里我再做下处理先把content为空的过滤掉在process_item文件里处理
可以导入from scrapy.extensions import DropItem这个类用来丢弃item
from scrapy.extensions import DropItem #丢弃itemclass NewsproPipeline:def process_item(self, item, spider):#加个判断条件content为空的不返回if not item[content]:DropItem(content不能为空丢弃)else:print(item:::, item)return item #一定要记得加return进行传递
再重新run一下bin文件在控制台搜索一下为空的content显示0个说明已经过滤成功了 到这里就已经使用scrapy框架完成了整个爬虫过程请求url - 解析数据 - 清洗数据
下一章接着一起来学习scrapy框架的进一步学习哦~