CrawlScrapy框架爬取Boos直聘職位資訊
阿新 • • 發佈:2018-12-02
寫在前面和推薦學習
零基礎: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分佈爬蟲