版权声明:转载请声明出处,谢谢! https://blog.csdn.net/qq_31468321/article/details/83218705
写在前面和推荐学习
零基础:21天搞定Python分布爬虫
在本文中使用CrawlScrapy框架结合代理来实现对Boos直聘职位信息的爬取。
简单说明Scrapy框架
主要流程
1.爬虫发送一个请求给引擎
2.引擎将这个请求发送给调度器
3.调度器按照一定的方式进行整理,在将请求发送给引擎
4.引擎再次将请求发送给下载器中间件,去到网络中请求资源进行下载
5.下载之后封装为Response对象,返回给引擎
6.引擎在将Resonse发送给爬虫提取信息,如果请求正常这时爬虫返回一个ITEMS,否则返回一个Resqust对象给引擎,再次进入调度器
7.这个items/Request返回给引擎,引擎将ITEMS对象发送给Pipeline对数据进行保存
爬虫结构
本次爬虫使用的是CrawlScrapy框架,这个框架继承了Scrapy框架,流程一致,不同的是,CrawlScrapy框架中有一个链接提取器,通过正则表达式进行匹配页面中所有的URL,有点广度优先的思想。
链接提取器如下:
rules = (
#c101010100/?query=数据分析&page=2
Rule(LinkExtractor(allow=r'.+/\?query=python&page=\d'), follow=True),
#https://www.zhipin.com/job_detail/9f625f605d18d1661XN_39--GFU~.html
Rule(LinkExtractor(allow=r'.+/job_detail/.+'), callback='parse_item', follow=False),
)
在此次爬虫中主要实现了如下几个部分:
- 爬虫部分,解析提取网页
- 实现了链接提取器中的规则,确定了需要爬取那些网页
- 实现了下载器中间件部分,在该部分添加了代理的功能,这样即使IP被封也可以继续爬取信息
- 实现了Pipeline的部分,对获取到的数据进行的保存,将数据保存到了JSON文件中
爬虫部分
实现了一个类,继承了CrawlSpider类,再定义规则时,主要获取列表页信息和详情页信息,再列表页中获取详情页的URL,主要对详情页的信息进行解析,提取详情页中的主要信息,返回ITEMS
代码如下:
class BossSpiderSpider(CrawlSpider):
name = 'boss_spider'
allowed_domains = ['www.zhipin.com']
start_urls = ['https://www.zhipin.com/job_detail/?query=python&page=1']
rules = (
#匹配列表页URL
#c101010100/?query=数据分析&page=2
Rule(LinkExtractor(allow=r'.+/\?query=python&page=\d'), follow=True),
#匹配详情页URL
#https://www.zhipin.com/job_detail/9f625f605d18d1661XN_39--GFU~.html
Rule(LinkExtractor(allow=r'.+/job_detail/.+'), callback='parse_item', follow=False),
)
def parse_item(self, response):
title = response.xpath("//div[@class='name']/h1/text()").get()
salary = response.xpath("//span[@class='badge']/text()").get()
if salary:
salary = salary.strip()
else:
salary = ''
company_div = response.xpath("//div[@class='info-company']")
company_name = company_div.xpath("./h3[@class='name']/a/text()").get()
company_info = company_div.xpath("./p[1]//text()").getall()
primary_info = response.xpath("//div[@class='info-primary']/p[1]/text()").getall()
city = primary_info[0]
work_time = primary_info[1]
education = primary_info[2]
datail_count = response.xpath("//div[@class='detail-content']//div[@class='text']/text()").getall()
item = BoosDemoItem(
title = title,
salary = salary,
company_name = company_name,
company_info = company_info,
city = city,
work_time = work_time,
education = education,
datail_count = datail_count
)
yield item
下载器中间件部分
在下载器中间件这里实现了两个类,分别为请求头中间件UserAgentMiddleware,和IP代理中间件IPProxyMiddlewares
请求头中间件:
在列表中保存多个请求头,在每次请求的时候随机选择一个。
#process_request接口会在请求开始的时候执行
def process_request(self,request,spider):
User_Agent = random.choice(self.Useragent)
request.headers['User-Agent'] = User_Agent
IP代理中间件:
判断每次URL失效的时候重新请求一个IP代理。重新返回爬取
class IPProxyMiddlewares(object):
def __init__(self):
self.currenu_proxy = None
self.lock = DeferredLock()
def process_request(self,request,spider):
if 'proxy' not in request.meta or self.currenu_proxy.is_expire:
self.update_proxy()
request.meta['proxy'] = self.currenu_proxy.proxy
def update_proxy(self):
self.lock.acquire()
if not self.currenu_proxy or self.currenu_proxy.is_expire or self.currenu_proxy.block:
self.url = 'http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=11&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1®ions='
resp = requests.get(url=self.url)
info_text = resp.text
info = json.loads(info_text)
if len(info['data'])>0:
data = info['data'][0]
self.currenu_proxy = Ipproxy(data)
self.lock.release()
#代理请求之后返回的时候执行
def process_response(self,request,response,spider):
if 'captcha' in response.url or response.status != 200 :
if not self.currenu_proxy.block:
self.currenu_proxy.block = True
self.update_proxy()
#request.meta['proxy'] = self.currenu_proxy.get_proxy()
return request
return response
class Ipproxy(object):
def __init__(self,data):
self.ip = data['ip']
self.port = data['port']
expire_time_str = data['expire_time']
print(self.ip, self.port, expire_time_str)
self.block = False
# 117.94.113.20 4734 2018-10-18 23:23:13
year, month, day = expire_time_str.split()[0].split('-')
hour, minute, scend = expire_time_str.split()[1].split(':')
self.expire_time = datetime(year=int(year), month=int(month), day=int(day), hour=int(hour), minute=int(minute),second=int(scend))
self.proxy = "https://{}:{}".format(self.ip,self.port)
@property
def is_expire(self):
if (self.expire_time - datetime.now()) < timedelta(seconds=5):
return True
else:
return False
Pipeline的部分
打开一个JSON文件,将数据保存到JSON文件中
#按行写入数据
from scrapy.exporters import JsonLinesItemExporter
class BoosDemoPipeline(object):
def __init__(self):
self.boss_fp = open("boss_python_fp.json",'wb')
self.boos_exporter = JsonLinesItemExporter(self.boss_fp,ensure_ascii = False)
def process_item(self, item, spider):
self.boos_exporter.export_item(item)
def close_item(self,spider):
self.boss_fp.close()
推荐学习
【知了课堂】零基础:21天搞定Python分布爬虫