scrapy爬虫入门学习笔记(一)
scrapy的安装很简单,网上有大量相关的教程,自行搜索。
1.scrapy框架
- 引擎(scrapy engine)
处理系统的数据流,触发事件
- 调度器(scheduler)
接受引擎发来的请求,压入队列,并在引擎再次请求时返回。可以想象成一个URL的优先队列,由它决定下一个抓取的网址是什么,并进行去重。
- 下载器(downloader)
用于下载网页内容,并返回给spider,下载器建立于twisted这个高效的异步模型之上
- 爬虫(spiders)
从特定网页爬取自己想要的信息,即item,提交给引擎
- 管道(pipeline)
处理从网页中提取的item,对信息进行存储、写等处理
- 下载中间件(downloader middlewares)
处理引擎和下载器之间的请求和响应
- 爬虫中间件(spider middlewares)
处理spider的响应输入和请求输出
- 调度中间件(scheduler middlewares)
对引擎的请求进行调度处理
2.scrapy项目文档目录
tree
- scrapy.cfg:项目的配置文件
- items.py:项目的目标文件。
- pipelines.py:项目的管道文件,作用就一个,处理item字段
- settings.py:项目的设置文件
- spiders:存储爬虫代码目录
items.py
- 定义结构化的字段,用于保存爬取到的数据,类似于python中的字典,但提供了额外的保护防止错误。
- 创建一个scrapy.Item类,定义类型为scrapy.Field的类属性来定义一个Item
- 创建一个ITcastItem类,构建Item模型
import scrapy
class ItcastItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name=scrapy.Field()
# l老师的职称
title=scrapy.Field()
# 老师信息
info=scrapy.Field()
# pass
itcast.py
- 爬虫python文件
- 定义一个ITcastSpider类,继承自scrapy.spider
- 需要name、start_urlds和parse
- parse函数中的xpath是xml文档查找信息的语言,可用于对xml元素和属性值进行遍历
class ItcastSpider(scrapy.Spider):
# 爬虫名,启动爬虫时需要的必须参数
name = 'itcast'
# 爬取域范围,允许爬虫在这个域名下进行爬取(可选)
# allowed_domains = []
# 起始URL列表,爬虫执行后第一批请求,将从这个列表中获取
start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
def parse(self, response):
node_list=response.xpath("//div[@class='li_txt']")
# 存储所有的items字段
items=[]
for node in node_list:
# 创建item字段,用来存储信息
item=ItcastItem()
# xpath对象转成Unicode字符串,使用extract()
name=node.xpath("./h3/text()").extract()
title=node.xpath("./h4/text()").extract()
info=node.xpath("./p/text()").extract()
item['name']=name[0]
item['title']=title[0]
item['info']=info[0]
items.append(item)
# 返回给引擎了
return items
yield item//item返回给pipeline
- yield和return的区别:yield提交但并不返回,依然继续执行。return之后函数就结束了。yield只是在程序执行中提交一个结果。如果所有数据全部保存在一个列表中,等到程序执行完毕后return,这样内存开销会很大,而且意外中断数据就丢失了。
pipelines.py和settings.py
Item 在spider中收集到后,将会传递到pipeline,pipeline组件按照响应码的顺序处理item,主要包含以下操作:
- 验证爬取的数据,比如是否包含某些字段,如name
- 查重并丢弃,URL的查重由调度器完成。数据的查重需要自己做,使用集合set
- 将爬取的结果保存到文件或数据库中。
#pipelines.py
import json
import codecs
class ItcastPipeline(object):
# 第二个是必选的,1和3是可选的,如果需要操作本地磁盘就需要1和3
# 初始化只执行一次
def __init__(self):
self.f=codecs.open("itcast_pipeline.json",'w',encoding='utf-8')
# 获取每一个item
def process_item(self, item, spider):
content=json.dumps(dict(item),ensure_ascii=False)+', \n'
self.f.write(content)
# 返回一个item给引擎,告诉引擎item已经处理好了,可以处理下一个item
# 如果有其他管道文件,就会交给下一个item
# 也就是说每一个管道类都需要一个returnitem
return item
# 爬虫关闭也只执行一次
def close_spider(self,spider):
self.f.close()
# 可以定义处理数据库的管道文件,但是需要在setings.py中设定
pipelines.py中的设定需要在settings.py中对应的设置才会生效。
#setings.py
#配置pipeline,每个管道类后跟一个响应码,依照从小到大优先
ITEM_PIPELINES = {
'ITcast.pipelines.ItcastPipeline': 300,
}
3.项目初体验
项目目的
爬取Tencent招聘信息,URL:https://hr.tencent.com/position.php?&start=0
创建项目
scrapy startproject tencent
items.py
首先在TencentItem类中定义字段,确定要哪些信息
class TencentItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
positionName=scrapy.Field()
positiontype=scrapy.Field()
requireNum=scrapy.Field()
positionLocation=scrapy.Field()
positionTime=scrapy.Field()
# pass
创建spider
scrapy genspider Tencent "tencent.com"
写spider类
# -*- coding: utf-8 -*-
import scrapy
from tencent.items import TencentItem
class TencentSpider(scrapy.Spider):
name = 'Tencent'
allowed_domains = ['tencent.com']
baseurl='https://hr.tencent.com/position.php?&start='
offset=0
start_urls=[]
for i in range(393):
start_urls.append(baseurl+str(offset+10*i))
def parse(self, response):
#xpath读取节点列表
node_list=response.xpath("//tr[@class='even'] | //tr[@class='odd']")
# print(len(node_list))
for node in node_list:
item=TencentItem()
item['positionName']=node.xpath("./td[1]/a/text()").extract()[0]
item['positionLink']=node.xpath("./td[1]/a/@href").extract()[0]
if len(node.xpath("./td[2]/text()")):
item['positiontype']=node.xpath("./td[2]/text()").extract()[0]
item['requireNum']=node.xpath("./td[3]/text()").extract()[0]
item['positionLocation']=node.xpath("./td[4]/text()").extract()[0]
item['positionTime']=node.xpath("./td[5]/text()").extract()[0]
yield item
# if self.offset<3930:
# self.offset+=10
# url=self.baseurl+str(self.offset)
# yield scrapy.Request(url,callback=self.parse)
这里可以采用多种方法继续爬取下一页的数据,可以采用yield scrapy.Request(url,callback=self.parse)
提交迭代的URL给引擎。也可以把所有的URL一并放到start_urls
里面。后一种方法采用并发执行,效率更高。
写pipeline类
import json
class TencentPipeline(object):
def __init__(self):
self.f=open("tencent.json",'w',encoding='utf-8')
def process_item(self, item, spider):
contents=json.dumps(dict(item),ensure_ascii=False)+',\n'
self.f.write(contents)
return item
def close_spider(self,spider):
self.f.close()
执行
scrapy crawl Tencent