Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。
scrapy文档
scrapy中每个模块的作用:
引擎(engine):负责数据和信号在不腰痛模块间的传递
调度器(scheduler):实现一个队列,存放引擎发过来的request请求对象
下载器(downloader):发送引擎发过来的request请求,获取响应,并将响应交给引擎
爬虫(spider):处理引擎发过来的response,提取数据,提取url,并交给引擎
管道(pipeline):处理引擎传递过来的数据,比如存储
下载中间件(downloader middleware):可以自定义的下载扩展,比如设置代理ip
爬虫中间件(spider middleware):可以自定义request请求和进行response过滤,与下载中间件作用重复
import scrapy
class ItcastSpider(scrapy.Spider): # 继承scrapy.spider
# 爬虫名字
name = 'itcast'
# 允许爬取的范围
allowed_domains = ['itcast.cn']
# 开始爬取的url地址
start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
# 数据提取的方法,接受下载中间件传过来的response
def parse(self, response):
# scrapy的response对象可以直接进行xpath
names = response.xpath('//div[@class="tea_con"]//li/div/h3/text()')
print(names)
# 获取具体数据文本的方式如下
# 分组
node_list = response.xpath('//div[@class="tea_con"]//li')
# 遍历所有教师结点
for node in node_list:
# temp={}
#实例化
temp=MyspiderItem()
# xpath方法返回的是选择器对象列表[]
#.extract()用于从选择器对象提取数据,多个值的列表
# 如果xpath结果是只含有一个值的列表,用extract_first()
temp["name"]=node.xpath('./div[2]/h3/text()').extract_first()
temp["title"]=node.xpath('./div[2]/h4/text()')[0].extract()
temp["desc"]=node.xpath('./div[2]/p/text()')[0].extract()
print(temp)
yield temp #爬虫中返回数据通常用yield,翻页方便。return没法继续走
注意:
利用管道pipeline来处理(保存)数据
import json
class ItcastPipeline():
# 爬虫文件中提取数据的方法每yield一次item,就会运行一次
# 该方法为固定名称函数
def process_item(self, item, spider):
print(item)
return item
ITEM_PIPELINES = {
'myspider.pipelines.ItcastPipeline': 400
}
配置项中键为使用的管道类,管道类使用.进行分割,第一个为项目目录,第二个为文件,第三个为定义的管道类。
配置项中值为管道的使用顺序,设置的数值约小越优先执行,该值一般设置为1000以内。
命令:在项目目录下执行scrapy crawl <爬虫名字>
示例:scrapy crawl itcast
class MyspiderItem(scrapy.Item):
name = scrapy.Field() # 讲师的名字
title = scrapy.Field() # 讲师的职称
desc = scrapy.Field() # 讲师的介绍
from myspider.items import MyspiderItem # 导入Item,注意路径
...
def parse(self, response)
item = MyspiderItem() # 实例化后可直接使用
item['name'] = node.xpath('./h3/text()').extract_first()
item['title'] = node.xpath('./h4/text()').extract_first()
item['desc'] = node.xpath('./p/text()').extract_first()
print(item)
注意:
实现方法:
通过爬取网易招聘的页面的招聘信息,学习如何实现翻页请求
地址:https://hr.163.com/position/list.do
思路分析:
注意:
1. 可以在settings中设置ROBOTS协议
# False表示忽略网站的robots.txt协议,默认为True
ROBOTSTXT_OBEY = False
2. 可以在settings中设置User-Agent:
# scrapy发送的每一个请求的默认UA都是设置的这个User-Agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
......
# 提取下一页的href
next_url = response.xpath('//a[contains(text(),">")]/@href').extract_first()
# 判断是否是最后一页
if next_url != 'javascript:void(0)':
# 构造完整url
url = 'https://hr.163.com/position/list.do' + next_url
# 构造scrapy.Request对象,并yield给引擎
# 利用callback参数指定该Request对象之后获取的响应用哪个函数进行解析
yield scrapy.Request(url, callback=self.parse)
......
scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])
参数解释
meta的作用:meta可以实现数据在不同的解析函数中的传递。
在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:
def parse(self,response):
...
yield scrapy.Request(detail_url, callback=self.parse_detail,meta={"item":item})
...
def parse_detail(self,response):
#获取之前传入的item
item = resposne.meta["item"]
特别注意
网易项目
提取岗位及岗位职责要求,meta传递参数
wangyi/spiders/job.py
import scrapy
from wangyi.items import WangyiItem
class JobSpider(scrapy.Spider):
name = 'job'
# 2.检查修改allowed_
allowed_domains = ['163.com']
# 1.修改起始start_url
start_urls = ['https://hr.163.com/position/list.do']
def parse(self, response):
# 提取数据
# 获取所有职位结点列表
node_list = response.xpath('//tbody/tr')
# node_list=response.xpath('//*[@class="position-tb"]/tbody/tr')
print(len(node_list))
# 遍历结点列表,过滤
for num, node in enumerate(node_list):
# print(num,node)
# 设置过滤条件
if num % 2 == 0:
item = WangyiItem()
item["name"] = node.xpath('./td[1]/a/text()').extract_first()
# response.urljoin()用于拼接相对路径的url,可以理解成自动补全
# item['link']=response.urljoin(node.xpath('./td[1]/a/text()').extract_first())
item["link"] = 'https://hr.163.com/' + node.xpath('./td[1]/a/@href').extract_first()
item["depart"] = node.xpath('./td[2]/text()').extract_first()
item["category"] = node.xpath('./td[3]/text()').extract_first()
item["type"] = node.xpath('./td[4]/text()').extract_first()
item["address"] = node.xpath('./td[5]/text()').extract_first()
item["num"] = node.xpath('./td[6]/text()').extract_first().strip() # 用于去除字符串中的空格
item["date"] = node.xpath('./td[7]/text()').extract_first()
# yield item
# 构建详情页面的请求
yield scrapy.Request(
url=item['link'],
callback=self.parse_deatil,
meta={'item': item}
)
# 模拟翻页
part_url = response.xpath('/html/body/div[2]/div[2]/div[2]/div/a[last()]/@href').extract_first()
# 判断终止条件
if part_url != 'javascript:void(0)':
next_url = response.urljoin(part_url)
yield scrapy.Request(
url=next_url,
callback=self.parse
)
def parse_deatil(self, response):
# print(response.meta['item'])
# 提取剩余字段的数据
item = response.meta['item']
item['duty'] = response.xpath(
'/html/body/div[2]/div[2]/div[1]/div/div/div[2]/div[1]/div/text()').extract() # 数据为多个
item['require'] = response.xpath(
'/html/body/div[2]/div[2]/div[1]/div/div/div[2]/div[2]/div/text()').extract() # 数据为多个
# print(item)
# 返回给引擎
yield item
wangyi/items.py
import scrapy
class WangyiItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
link = scrapy.Field()
depart = scrapy.Field()
category = scrapy.Field()
type = scrapy.Field()
address = scrapy.Field()
num = scrapy.Field()
date = scrapy.Field()
duty = scrapy.Field()
require = scrapy.Field()
wangyi/pipeline.py
import json
class WangyiPipeline:
def __init__(self):
self.file=open('wangyi.json','w')
def process_item(self, item, spider):
item=dict(item)
str_data=json.dumps(item,ensure_ascii=False)+',\n'
self.file.write(str_data)
return item