就是一个集成了很多功能并且具有很强通用性的一个项目模板。
专门学习框架封装的各种功能的详细用法。
爬虫中封装好的一个明星框架。
功能:
- 高性能的持久化操作
- 异步的数据下载操作
- 高性能的数据解析操作
- 分布式操作
linux和mac操作系统:pip install scrapy
windows系统:
pip install wheel
下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
pip install pywin32
pip install scrapy
测试:在终端里录入scrapy指令,没有报错即表示安装成功!
pip install wheel
pip install pywin32
pip install scrapy
一开始安装失败,系统提示让我更新pip,更新pip后从新安装成功。
更新pip的命令:
python -m pip install --upgrade pip
- 通过终端指令创建一个工程:
scrapy startproject xxxPro
- cd xxxPro
- 在spiders子目录中创建一个爬虫文件:
scrapy genspider spiderName 域名
- 执行工程:
scrapy crawl spiderName
【不显示日志:scrapy crawl spiderName --nolog
】
spiders文件里一定要放一个.py的爬虫文件
import scrapy
class FirstSpider(scrapy.Spider):
# 爬虫文件的名称 :就是爬虫源文件的唯一标识,即不能够重复
name = 'first'
# 允许的域名:用来限定start_urls列表中哪些url可以进行请求发送,一般该字段不用
# allowed_domains = ['www.xxx.com']
# 起使的url列表:该列表中存放的url会被scrapy自动进行请求的发送,可以有多个url
start_urls = ['https://www.baidu.com/','https://www.sogou.com/']
# 用于数据解析 response参数表示的就是请求成功之后对应的响应对象
# parse方法调用的次数由start_urls中url的个数决定的
def parse(self, response):
pass # 将print(response代替pass进行验证)
发现上述输出并没有有关start_urls中url的相关内容,
修改settings.py
中ROBOTSTXT_OBEY = False
不显示日志:
scrapy crawl spiderName --nolog
在settings.py中加入LOG_LEVEL ='ERROR'
,显示指定类型的日志信息
import requests
if __name__ == "__main__":
# 1. 指定url
url = "https://www.sogo.com/"
# 2. 发起请求
response = requests.get(url=url)
# get方法灰返回一个响应对象
# 3. 获取相应数据 .text返回的是字符串形式的响应数据
page_text = response
print(page_text)
scrapy框架的基本流程操作完毕后,一定要对settings.py文件进行如下操作:
- 修改USER_AGENT
- ROBOTSTXT_OBEY = False
- LOG_LEVEL = 'ERROR’
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
# 数据解析
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('//div[contains(@class,"col1")]/div')
# print('div_list:',div_list)
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有
标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
print(author, content)
break # 只输出一次用来查看
xpath如何取包含多个class属性
- 基于终端指令
要求:只可以将parse方法的返回值存储到本地的文本文件中【往数据库中存储是不行的】- 基于管道⭐⭐
- 数据解析
- 在Item类中定义相关的属性
- 将解析的数据封装到item类型的对象【使用items.py文件】
- 将Item类型对象提交给管道进行持久化存储操作
- 在管道类的process_item中呀将其接收到的item对象中存储的数据进行持久化存储操作【使用pipelines.py】
- 在配置文件中开启管道【scrapy默认情况是没有开启管道功能需要手动开启】
例如第二章中爬取xx百科中的段子进行存储的代码如下:
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
# 数据解析
def parse(self, response):
div_list = response.xpath('//div[contains(@class,"col1")]/div')
all_data = [] # 存储所有数据
# print('div_list:',div_list)
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有
标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
dic = {
'author':author,
'content':content
}
all_data.append(dic)
return all_data
现在可以通过终端指令对parse方法的返回值进行持久化存储
scrapy crawl qiubai -o ./qiubai.csv
【scrapy crawl spiderName -o filePpath
】- 注意:持久化存储的文件类型是有限制的
- 优点: 简洁高效便捷
- 缺点:局限性比较强(数据只能存储到指定文件中)
使用管道进行爬取xx百科中的段子
现在根据流程进行一步一步的操作
在qiubai.py文件进行数据解析
import scrapy
from qiubaipro.items import QiubaiproItem
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('//div[contains(@class,"col1")]/div')
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text() ').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有
标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
# 3. 将解析的数据封装到item类型的对象
item = QiubaiproItem()
# 获取值用[]而不是. ⭐⭐
item['author'] = author
item['content'] = content
# 4. 将Item类型对象提交给管道进行持久化存储操作
yield item
在items.py文件中操作
import scrapy
class QiubaiproItem(scrapy.Item):
# define the fields for your item here like:
author = scrapy.Field()
content = scrapy.Field()
pass
在pipelines.py文件中操作
from itemadapter import ItemAdapter
class QiubaiproPipeline:
fp = None
# 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次⭐
def open_spider(self,spider):
print('开始爬虫。。。。。。')
self.fp = open('./qiubai.txt','w',encoding='utf-8')
# 专门用来处理Item类型对象的
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一次item就会被调用一次⭐⭐⭐
def process_item(self, item, spider):
author = item['author']
content = item['content']
self.fp.write(author +":"+content+'\n')
return item
# 只会调用一次⭐
def close_spider(self,spider):
print('结束爬虫!')
self.fp.close()
修改author的代码即可:author = div.xpath(’./div[1]/a[2]/h2/text()
| ./div[1]/span/h2/text()
').extract_first()
在settings.py中操作
- 好处:通用性强,可以存储任意的文件或者数据库
- 缺点:编码流程有些繁琐
将爬取到的数据一份存储到本地,一份存储到数据库,如何实现?
使用管道文件中的管道类:
- 一个管道类对应一组数据存储到一个平台或者载体中
- 爬虫文件中的item只会给管道文件中第一个被执行的管道类接收
- pipelines.py中的process_item中的return item表示传递给下一个即将被执行的管道类【优先级高的传递给优先级低的】
同样根据编码流程进行编写,再次不再一一展示,只是展示核心代码
对pipelines.py操作
from itemadapter import ItemAdapter
import pymysql
class QiubaiproPipeline:
fp = None
# 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次
def open_spider(self,spider):
print('开始爬虫。。。。。。')
self.fp = open('./qiubai.txt','w',encoding='utf-8')
# 专门用来处理Item类型对象的
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一次item就会被调用一次
def process_item(self, item, spider):
author = item['author']
content = item['content']
self.fp.write(author +":"+content+'\n')
return item # 就会传递给下一个即将被执行的管道类
def close_spider(self,spider):
print('结束爬虫!')
self.fp.close()
# 管道文件中一个管道类对应一组数据存储到一个平台或者载体中
class mysqlPipeline(object):
conn = None
cursor = None # 游标对象
def open_spider(self,spider):
self.conn = pymysql.Connect(host='127.0.0.1',user='test',password='123456',db='testdb',charset='utf8')
def process_item(self, item, spider):
self.cursor = self.conn.cursor()
try:
self.cursor.execute('insert into qiubai values("%s","%s")'%(item["author"],item["content"]))
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback() # 出现错误,则回滚即这个事务从来没有执行过一样
# rollback()数据回滚的作用就是确保数据库操作的原子性问题,多次操作要么都执行,要么都不执行
return item
def close_spider(self,spider):
self.cursor.close() # 关闭游标
self.conn.close() # 关闭数据库
复习python对mysql数据库的操作:
- Python通过sql语句操作MySQL数据库
- python中实现Mysql数据回滚rollback()以及原理分析
全栈数据:就是将网站中某板块下的全部页码对应的页面数据进行爬取
爬取当前页的名称
- 将所有页面的url添加到start_urls列表中,列表中的元素会被自动请求发送【不推荐】
- 自行手动编码进行请求发送【推荐】⭐
yield scrapy.Request(url,callback)
:callback专门用于数据解析
import scrapy
class XiaohuaSpider(scrapy.Spider):
name = 'xiaohua'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://nice.ruyile.com/?f=5']
# 生成一个通用的模板(不可变)
url = 'https://nice.ruyile.com/?f=5&p=%d'
page_num =2
# 基于全栈数据的爬取
def parse(self, response):
div_list = response.xpath('/html/body/div[4]/div[1]/div[2]/div[@class="tp_list"]')
for div in div_list:
# scrapy中xpath表达式返回的是Selector
img_name = div.xpath('./div[2]/a[1]/text()').extract_first()
print(img_name)
# 一共117页 用5测试
if self.page_num <= 5:
new_url = format(self.url%self.page_num)# 不能使用self.new_url
self.page_num += 1
# 手动请求发送:callback回调函数是专门用于数据解析
yield scrapy.Request(url=new_url,callback=self.parse)
scrapy的基本使用我们已经掌握,但是各位心中一定会有些许的疑问,我们在编写scrapy工程的时候,我们只是在定义相关类中的属性或者方法,但是我们并没有手动的对类进行实例化或者手动调用过相关的方法,那么这些操作都是谁做的呢?接下来我们就来看看scrapy的五大核心组件的工作流程,然后大家就会上述的疑问有基本了解了。
用来处理整个系统的数据流处理, 触发事务(框架核心)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
- 产生url,对url进行手动发送
- 进行数据解析
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
在某些情况下,我们爬取的数据不在同一个页面中,例如,我们爬取一个电影网站,电影的名称,评分在一级页面,而要爬取的其他电影详情在其二级子页面中。这时我们就需要用到请求传参。
如果爬取解析的数据不在同一张页面中。【
深度爬取
】
爬取该网页的岗位名称,岗位描述
要爬取的这两个信息并没有在同一页面中,因此要使用请求传参,即
深度爬取
在进行本节练习的时候,可能由于反爬机制导致包含岗位信息和岗位描述的li标签列表无法获得。因此,最终没有结果显示,但还是将源码放在这里,供大家阅读和学习!
import scrapy
from bossPro.items import BossproItem
class BossSpider(scrapy.Spider):
name = 'boss'
# allowed_domains = ['www.xxx.com']
# 首页放进去
start_urls = ['https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=']
url = 'https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=%d'
page_num = 2
# 用于解析详情页中的岗位描述
def parse_detail(self, response):
item = response.meta['item']
job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract()
job_desc = ''.join(job_desc)
# print(job_desc)
item['job_desc'] = job_desc
yield item
# 用于解析首页的岗位名称
def parse(self, response):
# 查看响应状态码
print('code:',response.status)
li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
print('li_list:',li_list)
for li in li_list:
item = BossproItem()
job_name = li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/text()').extract_first()
item['job_name'] = job_name
# print(job_name)
detail_url = 'https://www.zhipin.com/' + li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/@href').extract_first()
# 对详情页发起请求获取详情页的页面源码数据
# 手动请求的发送
# 请求传参
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={
'items':item})
# 分页操作,对其他页面进行爬取
# 一定要设置条件和 page_num+=1
if self.page_num <= 3:
new_url = format(self.url%self.page_num)
self.page_num += 1
yield scrapy.Request(new_url,callback=self.parse)
detail_url = 'https://www.zhipin.com/' + li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/@href').extract_first()
# 请求传参
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={
'items':item})
----------------------------------------------------------------------------------------------------
# 用于解析详情页中的岗位描述
def parse_detail(self, response):
item = response.meta['item']
job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract()
job_desc = ''.join(job_desc)
# print(job_desc)
item['job_desc'] = job_desc
yield item
url = 'https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=%d'
page_num = 2
# 分页操作,对其他页面进行爬取
# 一定要设置条件和 page_num+=1
if self.page_num <= 3:
new_url = format(self.url%self.page_num)
self.page_num += 1
yield scrapy.Request(new_url,callback=self.parse)
图片数据爬取之
ImagesPipeline
基于scrapy爬取字符串类型的数据和爬取图片类型的数据区别?
- 字符串:只需要xpath解析且提交管道持久化存储即可
- 图片:xpath解析出图片src的属性值。单独对图片地址发起请求获取图片二进制类型的数据
ImagesPipeline
:
只需要将img的src属性值进行解析,提交到管道,管道就会对图片的src进行请求发送获取图片的二进制类型数据,且还会帮我们进行持久化存储
需求:爬取该页面中的高清图片
- 数据解析(图片的地址)
- 将存储图片地址的item提交到制定的管道类
- 在管道中自定制一个基于
ImagesPipeLine
的一个管道类,并重写方法
get_media_requests()
file_path()
item_completed()
- 在配置文件中:
- 指定图片存储的目录:
IMAGES_STORE='./imgs'
- 指定开启的管道:自定制的管道类
import scrapy
from scrapy import item
from imgsPro.items import ImgsproItem
class ImgSpider(scrapy.Spider):
name = 'img'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://sc.chinaz.com/tupian/']
def parse(self, response):
div_list = response.xpath('//*[@id="container"]/div')
# print('div_list:',div_list)
for div in div_list:
src =div.xpath('./div[1]/a/img/@src2').extract_first()
print('src :',src )
最后发现打开浏览器后显示出来的图片的属性值是src,没有可视化显示的图片属性值是src2,由于scrapy爬取是无法可视化,因此使用scr2属性值。
该地址是短缺的,记得加上https://
import scrapy
from scrapy import item
from imgsPro.items import ImgsproItem
class ImgSpider(scrapy.Spider):
name = 'img'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://sc.chinaz.com/tupian/']
def parse(self, response):
div_list = response.xpath('//*[@id="container"]/div')
# print('div_list:',div_list)
for div in div_list:
# scrapy爬取不是可视化显示图片,因此使用伪属性src2
src = 'https:' + div.xpath('./div[1]/a/img/@src2').extract_first()
item = ImgsproItem()
item['src'] = src
yield item
# 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 scrapy.pipelines.images import ImagesPipeline ⭐⭐
import scrapy
# class ImgsproPipeline:
# def process_item(self, item, spider):
# return item
#ImagesPipeline专门用于文件下载的管道类,下载过程支持异步和多线程
class imgsPipeline(ImagesPipeline):
#对item中的图片进行请求操作
def get_media_requests(self, item, info):
# 手动发送请求
yield scrapy.Request(item['src'])
#指定图片存储的路径
def file_path(self, request, response=None, info=None):
# 获取图片的请求地址:request.url
# 将https://.../bpic24372_s.jpg后的bpic24372_s.jpg作为名称
url = request.url
image_name = url.split('/')[-1]
return image_name
def item_completed(self, results, item, info):
return item #该返回值会传递给下一个即将被执行的管道类
参考:scrapy图片数据爬取
下载中间件(Downloader Middlewares) 位于scrapy引擎和下载器之间的一层组件。
- 作用:批量拦截到整个工程中所有的请求和响应
- 拦截请求:
- UA伪装:
process_request
- 代理IP:
process_exception
–return reques
t
- 拦截响应:
- 篡改响应数据,响应对象
- 需求:爬取该网页的几个板块的新闻数据
我们
主要使用下载中间件处理请求
,一般会对请求设置随机的User-Agent ,设置随机的代理。目的在于防止爬取网站的反爬虫策略。
- (1)引擎将请求传递给下载器过程中, 下载中间件可以对请求进行一系列处理。比如设置请求的 User-Agent,设置代理等
- (2)在下载器完成将Response传递给引擎中,下载中间件可以对响应进行一系列处理。比如进行gzip解压等。
import scrapy
class MiddleSpider(scrapy.Spider):
# 爬取百度
name = 'middle'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://www.baidu.com/s?wd=ip']
def parse(self, response):
page_text = response.text
with open('ip.html','w',encoding='utf-8') as fp:
fp.write(page_text)
class MiddleproDownloaderMiddleware:
# User-Agent池
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
#可被选用的代理IP
PROXY_http = [
'153.180.102.104:80',
'195.208.131.189:56055',
]
PROXY_https = [
'120.83.49.90:9000',
'95.189.112.214:35508',
]
# 用于拦截请求
def process_request(self, request, spider):
# UA伪装
request.headers['User-Agent'] = random.choice(self.user_agent_list)
# 为了验证代理的操作是否生效
request.meta['proxy'] = 'http://61.178.149.237:59042 '
return None
# 拦截所有响应
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
# Must either;
# - return a Response object
# - return a Request object
# - or raise IgnoreRequest
return response
# 拦截异常
def process_exception(self, request, exception, spider):
# 使用代理
# 只有当自己的ip被服务器屏蔽掉时,需要使用代理IP
if request.url.split(':')[0] == 'http':
request.meta['proxy'] = 'http://' + random.choice(self.PROXY_http)
else:
request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
return request # 将修正之后的请求对象进行重新的请求发送
User-Agent池
作用:尽可能多的将scrapy工程中的请求伪装成不同类型的浏览器身份。
操作流程:
- 在下载中间件中拦截请求
- 将拦截到的请求的请求头信息中的UA进行篡改伪装
- 在配置文件中开启下载中间件
爬取该网页的几个板块的新闻数据(标题和内容)
- 通过该网页的首页解析出五大板块的详情页的url(没有动态加载,直接爬取)
- 每一个板块对应的新闻标题都是动态加载的
- 通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻内容
wangyi.py
- 实例化浏览器对象
- 解析板块的url
- 解析每一个板块页面中新闻标题和新闻详情页的url
- 解析每一个模板中每个标题对应的内容
item.py
定义所需要的itempipelines.py
用来处理数据的持久化存储,在这里我们仅仅输出middlewares.py
⭐⭐⭐
- 使用process_response进行响应拦截
- 使用spider爬虫对象对响应模块进行篡改
- 使用selenium获取动态加载出的新闻数据并将新的响应对象返回
import scrapy
from scrapy import item
from selenium import webdriver
from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://news.163.com/']
models_urls = [] # 存储五个板块详情页的url
# 实例化一个浏览器对象
def __init__(self):
self.bro = bro = webdriver.Chrome(executable_path=r'E:\Google\chromedriver')
# 解析五大板块对应的详情页的url
def parse(self, response):
li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
alist = [2,3,5,6] # 取出指定的li标签
for index in alist:
# 获取每个板块的url
model_url = li_list[index].xpath('./a/@href').extract_first()
self.models_urls.append(model_url)
# 依次对每一个板块对应得页面进行请求
for url in self.models_urls:
# print('================================================================')
# print("url:",url)
yield scrapy.Request(url,callback=self.parse_model)
# 解析每一个板块页面中新闻标题和新闻详情页的url
def parse_model(self,response):
div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div[1]/div/ul/li/div/div')
print('div_list:',div_list)
for div in div_list:
title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
# print('name:',name,'url:',url)
item = WangyiproItem()
item['title'] = title
yield scrapy.Request(url=url, callback = self.parse_detail,meta={
'item':item})
# 解析每一个模板中每个标题对应的内容
def parse_detail(self,response):
content = response.xpath('//*[@id="content"]/div[2]//text()').extract()
content = ''.join(content)
item = response.meta['item']
item['content'] = content
yield item
def closed(self,spider):
self.bro.quit()#关闭浏览器
引入的包
from scrapy.http import HtmlResponse
from time import sleep
# 该方法拦截五大板块对应的响应对象,进行篡改
# spider爬虫对象
def process_response(self, request, response, spider):
bro = spider.bro # 获取了在爬虫类中定义的浏览器对象
# 挑选出指定的响应对象进行篡改
# 通过url指定request
# 通过request指定response
if request.url in spider.models_urls:
bro.get(request.url) # 对5个板块对应的url进行请求
sleep(3)
page_text = bro.page_source # 获取页面源码数据,包含动态加载的新闻数据
# response # 五大板块对应的响应对象
# 针对定位到的这些response进行篡改
# 实例化一个新的响应对象(符合需求:包含动态加载出的新闻数据)代替原来旧的响应数据
# 如何获取动态加载出的新闻数据?selenium
new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
return new_response
else:
# response # 其他板块对应的响应对象
return response
# 管道用来处理数据的持久化存储
class WangyiproPipeline:
fp = None
# 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次
def open_spider(self,spider):
print('开始爬虫。。。。。。')
self.fp = open('./wangyi_news.txt','w',encoding='utf-8')
# 专门用来处理Item类型对象的
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一次item就会被调用一次
def process_item(self, item, spider):
title = item['title']
content = item['content']
self.fp.write(title +":"+content+'\n')
return item # 就会传递给下一个即将被执行的管道类
def close_spider(self,spider):
print('结束爬虫!')
self.fp.close()
Robots协议是国际互联网界通行的道德规范,基于以下原则建立:
- 搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权;
- 网站有义务保护其使用者的个人信息和隐私不被侵犯。
- 爬虫的"盗亦有道"-Robots协议
- robots协议
- Robots协议是什么?⭐
无法爬取岗位信息对应的标签列表
目的:获取li标签列表,从而获取岗位信息和岗位描述
查看响应状态码:
print('code:',response.status)
所以就很奇怪,明明响应状态码成功,但是却出现了无法爬取指定标签的问题
使用selenium可以爬取li标签列表,但是使用scrapy框架和requests模块无法得到li标签列表