scrapy 爬虫流程
scrapy 爬虫流程和一般的爬虫流程基本一样,发送url,响应提取url和数据,数据存储,url重新放到url队列中
Scrapy Engine(引擎) | 总指挥:负责数据和信号在不同模块之间传递 | scrapy实现 |
Scheduler(调度器) | 队列,存放engine发送过来的request请求 | scrapy实现 |
Downloader(下载器) | 下载引擎发送过来的请求,并返回给引擎 | scrapy实现 |
Spider(爬虫) | 处理引擎发送过来的reponse,提取数据,url,返回给引擎 | 自己手写 |
Item Pipeline(管道) | 处理引擎传送过来的数据,存储到mongodb | 自己手写 |
Downloader Middlewares (下载中间件) | 自定义下载扩展,例:设置代理IP,user-agent | 自己写相关类 |
Spider Middlewares (爬虫中间件) | 可自定义request请求和进行response过滤 | 一般不用手写 |
scrapy 工程创建
创建工程
scrapy startproject +项目名字
$ scrapy startproject spider_boss
创建爬虫
$ cd spider_boss
scrapy genspider +<爬虫名字> + <允许爬取的域名>
$ scrapy genspider zhipin zhipin.com
oniondeMacBook-Pro:spider onion$ tree spider_boss/ spider_boss/ ├── README.md ├── main.py 自己写的爬虫执行入口 ├── scrapy.cfg └── spider_boss ├── __init__.py ├── items.py 定义自己需要爬取的内容 ├── middlewares.py 下载中间件添加代理 ├── pipelines.py 管道处理数据存储 ├── settings.py 爬虫的设置文件,下载延时,日志等级等 └── spiders 自己定义的spider文件夹 ├── __init__.py ├── zhipin.py └── zhipin_test.py
scrapy文件解析
spider_boss.items.py
#spider_boss.items.py
class SpiderBossItem(scrapy.Item):
# define the fields for your item here like:
#需要爬取得内容
job_title = scrapy.Field() #一个字典
company = scrapy.Field()
money = scrapy.Field()
job_context = scrapy.Field()
detail_url=scrapy.Field()
#获取数据的时候,使用不同的item来存放不同的数据
#在数据交给pipeline 的时候,可以通过isnstance(item,SpiderBossItem)来判断属于哪个item,进行不同的数据(item)处理
spiders.zhipin.py
从选择器中提取字符串
1.extract() 返回一个包含有字符串数据的列表
2.extract_first() 返回列表中的第一个字符串
注意:
spider中的parse方法名不能修改
爬取得url必须是属于allowed_domains下的链接
response.xpath()返回的是一个含有selector对象的列表
scrapy.request():
scrapy.Request(url,[callback,meta,dont_filter=False,...]) 常用参数: callback:指定传入的url交给哪个解析函数去处理 meta:实现在不同解析函数中传递数据,meta 会默认带部分信息,如下载延迟,请求深度(settings中配置)等 dont_filter:让scrapy 是否过滤当前url,默认False,带去重功能,如果需要重复请求某个url,需要置True #scrap.Request()函数定义 class Request(object_ref): def __init__(self, url, callback=None, method='GET', headers=None, body=None, cookies=None, meta=None, encoding='utf-8', priority=0, dont_filter=False, errback=None, flags=None):
#spiders.zhiping.py
class ZhipinSpider(scrapy.Spider):
name = 'zhipin' #爬虫名 <scrapy crawl zhipin>
allowed_domains = ['zhipin.com'] #允许爬的域名,防止爬到其他网站
start_urls = ['http://zhipin.com/job_detail/?query=python'] #开始爬取的地址
def parse(self, response): #接收下载中间件传来的response
info_list = response.xpath('//div[@class="job-list"]//li')
for each in info_list:
item = SpiderBossItem() #item中定义需要爬去的内容
item["company"] = each.xpath('.//div[@class="company-text"]/h3//text()').extract_first()
item["job_title"] = each.xpath('.//div[@class="job-title"]/text()').extract_first()
item["money"] = each.xpath(".//span/text()").extract_first()
item["detail_url"] = each.xpath('.//div[@class="job-title"]/../@href').extract_first()
item["detail_url"] = "https://www.zhipin.com" + item["detail_url"]
# logger.warning(item)
# 回调处理职位详情页
yield scrapy.Request(
item["detail_url"],
callback=self.parse_context_detail,
meta={"item": item}# 将这里的item传送给parse_context_detail中继续处理,
#这里传过去的是同一个item,如果你不想下次的处理影响当前item,使用deepcopy 例: meta={"item":deepcopy(item)}
)
# next page
next_url = response.xpath("//a[@class='next']/@href").extract_first()
if next_url is not None:
next_url = "https://www.zhipin.com" + next_url
yield scrapy.Request(
next_url,
callback=self.parse
)
def parse_context_detail(self, response):
item = response.meta["item"] #接收parse中的item
item["job_context"] = response.xpath("//div[@class='job-sec']/div//text()").extract()
# print(item)
yield item #传送个管道处理数据
spider_boss.pipelines.py
管道权重越小,优先级越高
pipeline process_item()方法不能修改为其他名称
#spider_boss.pipelines.py
class SpiderBossPipeline(object):
def open_spider(self, spider): #爬虫开始会调用一次
#mongodb的配置个可以放在settings.py文件中管理
self.client = MongoClient("mongodb://127.0.0.1:27017/boss")
def close_spider(self, spider): #爬虫结束会调用
print("close ...")
print(spider.name)
self.client.close()
def process_item(self, item, spider): #item参数 spider yield item 过来的
if spider.name =="zhipin":
self.collection = self.client["boss"]["python"]
item["job_context"] = self.process_context(item["job_context"])
# collection.insert(item)
if isinstance(item, SpiderBossItem):
self.collection.insert(dict(item)) #数据存储到mongodb中
# print(item)
return item
def process_context(self, context):
# 处理 "\n" 和空字符串
context = [i.strip() for i in context if i.strip() != ""]
return context
管道文件写完后需要在settings.py中开启管道
spider_boss.settings.py中
#spider_boss.settings
#: 300 值越小优先级越高
ITEM_PIPELINES = {
'spider_boss.pipelines.SpiderBossPipeline': 300,
'spider_boss.pipelines.SpiderBossPipeline1': 301,
}
spider_boss.middlewares.py
from scrapy import signals
import random
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
class MyUserAgentMiddleware(object): #设置user-agent
def __init__(self):
pass
def process_request(self, request, spider):
# spider.logger.info(msg='now entring download midware')
agent = random.choice(settings.get('MY_USER_AGENT_LIST'))
if agent:
request.headers.setdefault(b'User-Agent',agent)
spider.logger.info(u'User-Agent is : {} {}'.format(request.headers.get('User-Agent'), request))
class IPProxyDownloadMiddleware(object): #设置IP代理
def process_request(self, request, spider):
ip_proxy = random.choice(settings.get('IP_PROXY_LIST')) #获取settings文件中ip代理列表
print('*'*20)
print(ip_proxy)
#添加代理需要在request的meta信息中添加proxy字段,
#代理形式:协议+ip+端口
request.meta["proxy"]=ip_proxy
spider_boss.settings.py中开启下载中间件和定义相关的user-agent列表和代理ip列表
MY_USER_AGENT_LIST=[
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
...
]
IP_PROXY_LIST=[
# "http://58.253.155.211:9999",
"https://61.189.242.243:55484",
# "http://175.44.158.72:9000"
]
DOWNLOADER_MIDDLEWARES = {
'spider_boss.middlewares.MyUserAgentMiddleware': 543,
'spider_boss.middlewares.IPProxyDownloadMiddleware': 542,
}