作者:TM0831
PS:如有需要Python學(xué)習(xí)資料的小伙伴可以加點(diǎn)擊下方鏈接自行獲取
http://t.cn/A6Zvjdun
在之前寫拉勾網(wǎng)的爬蟲的時(shí)候,總是得到下面這個(gè)結(jié)果(真是頭疼),當(dāng)你看到下面這個(gè)結(jié)果的時(shí)候,也就意味著被反爬了,因?yàn)橐恍┚W(wǎng)站會(huì)有相應(yīng)的反爬蟲措施,例如很多網(wǎng)站會(huì)檢測(cè)某一段時(shí)間某個(gè)IP的訪問次數(shù),如果訪問頻率太快以至于看起來不像正常訪客,它可能就會(huì)禁止這個(gè)IP的訪問:
對(duì)于拉勾網(wǎng),我們要找到職位信息的ajax接口倒是不難(如下圖),問題是怎么不得到上面的結(jié)果。
要想我們的爬蟲不被檢測(cè)出來,我們可以使用代理IP,而網(wǎng)上有很多提供免費(fèi)代理的網(wǎng)站,比如西刺代理、快代理、89免費(fèi)代理等等,我們可以爬取一些免費(fèi)的代理然后搭建我們的代理池,使用的時(shí)候直接從里面進(jìn)行調(diào)用就好了。然后通過觀察可以發(fā)現(xiàn),拉勾網(wǎng)最多顯示30頁職位信息,一頁顯示15條,也就是說最多顯示450條職位信息。在ajax接口返回的結(jié)果中可以看到有一個(gè)totalCount字段,而這個(gè)字段表示的就是查詢結(jié)果的數(shù)量,獲取到這個(gè)值之后就能知道總共有多少頁職位信息了。對(duì)于爬取下來的結(jié)果,保存在MongoDB數(shù)據(jù)庫中。
proxies.py
(爬取免費(fèi)代理并驗(yàn)證其可用性,然后生成代理池)
1 import requests
2 import re
3
4
5 class Proxies:
6 def __init__(self):
7 self.proxy_list=[]
8 self.headers={
9 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
10 "Chrome/45.0.2454.101 Safari/537.36",
11 'Accept-Encoding': 'gzip, deflate, sdch',
12 }
13
14 # 爬取西刺代理的國內(nèi)高匿代理
15 def get_proxy_nn(self):
16 proxy_list=[]
17 res=requests.get("http://www.xicidaili.com/nn", headers=self.headers)
18 ip_list=re.findall('<td>(\d+\.\d+\.\d+\.\d+)</td>', res.text)
19 port_list=re.findall('<td>(\d+)</td>', res.text)
20 for ip, port in zip(ip_list, port_list):
21 proxy_list.append(ip + ":" + port)
22 return proxy_list
23
24 # 驗(yàn)證代理是否能用
25 def verify_proxy(self, proxy_list):
26 for proxy in proxy_list:
27 proxies={
28 "http": proxy
29 }
30 try:
31 if requests.get('http://www.baidu.com', proxies=proxies, timeout=2).status_code==200:
32 print('success %s' % proxy)
33 if proxy not in self.proxy_list:
34 self.proxy_list.append(proxy)
35 except:
36 print('fail %s' % proxy)
37
38 # 保存到proxies.txt里
39 def save_proxy(self):
40 # 驗(yàn)證代理池中的IP是否可用
41 print("開始清洗代理池...")
42 with open("proxies.txt", 'r', encoding="utf-8") as f:
43 txt=f.read()
44 # 判斷代理池是否為空
45 if txt !='':
46 self.verify_proxy(txt.strip().split('\n'))
47 else:
48 print("代理池為空!\n")
49 print("開始存入代理池...")
50 # 把可用的代理添加到代理池中
51 with open("proxies.txt", 'w', encoding="utf-8") as f:
52 for proxy in self.proxy_list:
53 f.write(proxy + "\n")
54
55
56 if __name__=='__main__':
57 p=Proxies()
58 results=p.get_proxy_nn()
59 print("爬取到的代理數(shù)量", len(results))
60 print("開始驗(yàn)證:")
61 p.verify_proxy(results)
62 print("驗(yàn)證完畢:")
63 print("可用代理數(shù)量:", len(p.proxy_list))
64 p.save_proxy()
在middlewares.py中添加如下代碼:
1 class LaGouProxyMiddleWare(object):
2 def process_request(self, request, spider):
3 import random
4 import requests
5 with open("具體路徑\proxies.txt", 'r', encoding="utf-8") as f:
6 txt=f.read()
7 proxy=""
8 flag=0
9 for i in range(10):
10 proxy=random.choice(txt.split('\n'))
11 proxies={
12 "http": proxy
13 }
14 if requests.get('http://www.baidu.com', proxies=proxies, timeout=2).status_code==200:
15 flag=1
16 break
17 if proxy !="" and flag:
18 print("Request proxy is {}".format(proxy))
19 request.meta["proxy"]="http://" + proxy
20 else:
21 print("沒有可用的IP!")
然后還要在settings.py中添加如下代碼,這樣就能使用代理IP了:
1 SPIDER_MIDDLEWARES={
2 'LaGou.middlewares.LaGouProxyMiddleWare': 543,
3 }
在item.py中添加如下代碼:
1 import scrapy
2
3
4 class LaGouItem(scrapy.Item):
5 city=scrapy.Field() # 城市
6 salary=scrapy.Field() # 薪水
7 position=scrapy.Field() # 職位
8 education=scrapy.Field() # 學(xué)歷要求
9 company_name=scrapy.Field() # 公司名稱
10 company_size=scrapy.Field() # 公司規(guī)模
11 finance_stage=scrapy.Field() # 融資階段
在pipeline.py中添加如下代碼:
1 import pymongo
2
3
4 class LaGouPipeline(object):
5 def __init__(self):
6 conn=pymongo.MongoClient(host="127.0.0.1", port=27017)
7 self.col=conn['Spider'].LaGou
8
9 def process_item(self, item, spider):
10 self.col.insert(dict(item))
11 return item
在spiders文件夾下新建一個(gè)spider.py,代碼如下:
1 import json
2 import scrapy
3 import codecs
4 import requests
5 from time import sleep
6 from LaGou.items import LaGouItem
7
8
9 class LaGouSpider(scrapy.Spider):
10 name="LaGouSpider"
11
12 def start_requests(self):
13 # city=input("請(qǐng)輸入城市:")
14 # position=input("請(qǐng)輸入職位方向:")
15 city="上海"
16 position="python"
17 url="https://www.lagou.com/jobs/positionAjax.json?px=default&needAddtionalResult=false&city={}".format(city)
18 headers={
19 "Referer": "https://www.lagou.com/jobs/list_{}?city={}&cl=false&fromSearch=true&labelWords=&suginput=".format(codecs.encode(position, 'utf-8'), codecs.encode(city, 'utf-8')),
20 "Cookie": "_ga=GA1.2.2138387296.1533785827; user_trace_token=20180809113708-7e257026-9b85-11e8-b9bb-525400f775ce; LGUID=20180809113708-7e25732e-9b85-11e8-b9bb-525400f775ce; index_location_city=%E6%AD%A6%E6%B1%89; LGSID=20180818204040-ea6a6ba4-a2e3-11e8-a9f6-5254005c3644; JSESSIONID=ABAAABAAAGFABEFFF09D504261EB56E3CCC780FB4358A5E; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1534294825,1534596041,1534596389,1534597802; TG-TRACK-CODE=search_code; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1534599373; LGRID=20180818213613-acc3ccc9-a2eb-11e8-9251-525400f775ce; SEARCH_ID=f20ec0fa318244f7bcc0dd981f43d5fe",
21 "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36"
22 }
23 data={
24 "first": "true",
25 "pn": 1,
26 "kd": position
27 }
28 res=requests.post(url, headers=headers, data=data)
29 # 獲取相關(guān)職位結(jié)果數(shù)目
30 count=res.json()['content']['positionResult']['totalCount']
31 # 由于最多顯示30頁,也就是最多顯示450條職位信息
32 page_count=count // 15 + 1 if count <=450 else 30
33 for i in range(page_count):
34 sleep(5)
35 yield scrapy.FormRequest(
36 url=url,
37 formdata={
38 "first": "true",
39 "pn": str(i + 1),
40 "kd": position
41 },
42 callback=self.parse
43 )
44
45 def parse(self, response):
46 try:
47 # 解碼并轉(zhuǎn)成json格式
48 js=json.loads(response.body.decode('utf-8'))
49 result=js['content']['positionResult']['result']
50 item=LaGouItem()
51 for i in result:
52 item['city']=i['city']
53 item['salary']=i['salary']
54 item['position']=i['positionName']
55 item['education']=i['education']
56 item['company_name']=i['companyFullName']
57 item['company_size']=i['companySize']
58 item['finance_stage']=i['financeStage']
59 yield item
60 except:
61 print(response.body)
由于使用的是免費(fèi)代理,短時(shí)間內(nèi)就失效了,所以會(huì)碰上爬取不到數(shù)據(jù)的情況,所以推薦使用付費(fèi)代理。
為一名毫無開發(fā)經(jīng)驗(yàn)的非計(jì)算機(jī)出身的數(shù)據(jù)愛好者,初入此坑時(shí)深受爬蟲難學(xué)之苦,當(dāng)初未通Python之道,寫個(gè)scrapy框架就痛苦至極。想想現(xiàn)在大數(shù)據(jù)技術(shù)那么牛逼了,為什么我抓個(gè)數(shù)據(jù)還處處被封,后來又覺得是自己技術(shù)不夠強(qiáng)大。本文以拉勾網(wǎng)為例給大家介紹一款便捷快速的R語言爬蟲方法,通過Rvest包+SelectorGdaget選擇器即可輕松實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)抓取。
下載安裝Rvest包:
install.packages("Rvest")
library(Rvest)
要想全面了解Rvest包的朋友可以去查官方幫助文檔:
help(package="Rvest")
Selectorgadget插件作為一個(gè)輕便快捷的CSS選擇器,好用程度簡(jiǎn)直爆炸,鼠標(biāo)點(diǎn)擊幾下即可生成你想要抓取的html節(jié)點(diǎn)信息。這么一款神器,調(diào)用方法也是極其簡(jiǎn)單,打開任何一款搜索網(wǎng)頁,鍵入Selectorgadget,點(diǎn)擊第一個(gè)鏈接,也是Selectorgadget官方鏈接,拉到頁面底端倒數(shù)第二個(gè)鏈接,將其拖拽到你的瀏覽器收藏夾,待下次打開需要爬取的網(wǎng)頁時(shí)點(diǎn)擊即可啟用。
需拖拽的鏈接如圖(Or drag this link to your bookmark bar):
下次調(diào)用時(shí),打開需要抓取的網(wǎng)頁,點(diǎn)擊我們拖拽到收藏夾的Selectorgadget會(huì)在網(wǎng)頁右下角出現(xiàn)一個(gè)長(zhǎng)方形條框,點(diǎn)擊網(wǎng)頁中任何我們想抓取的信息,條框內(nèi)即可生成相應(yīng)的文本表達(dá)式,將這些文本表達(dá)式復(fù)制到Rvest包對(duì)應(yīng)的爬蟲函數(shù)中,即可輕松完成抓取。需要注意的是,使用Selectorgadget選擇節(jié)點(diǎn)信息是一個(gè)篩選的過程,其間需要將我們不需要的信息(點(diǎn)擊后變紅)重復(fù)點(diǎn)擊以刪除,留下需要的信息(綠色和黃色部分)。
我們選擇抓取拉勾網(wǎng)數(shù)據(jù)分析師崗位信息:
抓取代碼如下:
library(stringr)library(xml2) library(rvest) #加載包 i<-1:30#設(shè)定抓取頁數(shù) lagou_data<-data.frame()#創(chuàng)建數(shù)據(jù)框存儲(chǔ)數(shù)據(jù) #寫個(gè)循環(huán),對(duì)固定網(wǎng)頁結(jié)構(gòu)重復(fù)抓取 for (i in 1:30){ web<-read_html(str_c("https://www.lagou.com/zhaopin/shujufenxi/",i),encoding="UTF-8")#read_html函數(shù)解析網(wǎng)頁并規(guī)定編碼str_c函數(shù)對(duì)頁數(shù)循環(huán) job<-web%>%html_nodes("h2")%>%html_text()#"h2"即為Selectorgadget定位節(jié)點(diǎn)信息 job[16]<-NA job<-job[!is.na(job)]#將多余信息設(shè)置為NA并剔除 #以此類推,抓取崗位其他信息 company<-web%>%html_nodes(".company_name a")%>%html_text() inf1<-web%>%html_nodes(".p_bot .li_b_l")%>%html_text() inf2<-web%>%html_nodes(".industry")%>%html_text() temptation<-web%>%html_nodes(".li_b_r")%>%html_text()
#存儲(chǔ)以上信息
job_inf<-data.frame(job,company,inf1,inf2,temptation)
lagou_data<-rbind(lagou_data,job_inf)
}
write.csv(job_inf,file="D:/Rdata/datasets/job_inf.csv")#寫入數(shù)據(jù)
清洗整理后最終抓取部分?jǐn)?shù)據(jù)示例如圖:
用rvest包結(jié)合SelectorGadget 選擇器能夠快速實(shí)現(xiàn)R語言下的網(wǎng)絡(luò)數(shù)據(jù)抓取,并適當(dāng)結(jié)合stringr包中的字符串處理函數(shù)對(duì)網(wǎng)頁數(shù)據(jù)進(jìn)行清洗和整理,抓取過程省時(shí)省力,適合R語言和爬蟲入門的朋友使用學(xué)習(xí)。
End.
來源:公眾號(hào)“R語言中文社區(qū)”
運(yùn)行人員:中國統(tǒng)計(jì)網(wǎng)小編(微信號(hào):itongjilove)
微博ID:中國統(tǒng)計(jì)網(wǎng)
中國統(tǒng)計(jì)網(wǎng),是國內(nèi)最早的大數(shù)據(jù)學(xué)習(xí)網(wǎng)站,公眾號(hào):中國統(tǒng)計(jì)網(wǎng)
http://www.itongji.cn
始人許單單透露,拉勾網(wǎng)于2015年開始商業(yè)化,每年收入都有一倍以上的增長(zhǎng),2017年基本實(shí)現(xiàn)現(xiàn)金流平衡。今年,拉勾網(wǎng)終將盈利排上日程,并擬于2019年赴美上市。12月21日,拉勾銷售副總裁章維在2018年TOP雇主頒發(fā)典禮上宣布,2019年將著重關(guān)注B端用戶體驗(yàn)、成立企業(yè)體驗(yàn)部、擴(kuò)大銷售團(tuán)隊(duì)人員。(界面)