第一個完整爬蟲:爬取應屆生網所有職位的詳細資訊
在前面幾篇博文裡其實已經介紹了和Python爬蟲相關的很多基礎知識,包括基本的抓取網頁資訊,ip池的建立和使用,多程序在ip驗證中的使用,今天我們就把這些內容整合到一起,完成一個真正的爬蟲。
我們先來梳理一下思路,要爬取所有職位的詳細資訊,應該包括以下幾步:
1 爬取所有職位及其對應的url
2 對每一個職位所對應的url進行資訊提取
3 將資訊儲存在本地數控庫中
4 如果要防止ip被封還需要去爬取可用的ip並驗證
那現在就來一步步完成以上這些工作。首先要說的是,最近真的很迷requests這個庫,感覺比urllib簡潔太多了,用著就兩個字,酥胡!所以爬取所有職位對應的url程式碼如下:
#爬取職位及其對應url def getLink(url,proxy=''): response=requests.get(url,proxies=proxy) response.encoding='gbk' bs=BeautifulSoup(response.text) res=bs.find_all("td",{"class":"item1"}) links=[] for item in res: if 'http://my.yingjiesheng.com/job' in item.find('a').attrs['href']: links.append(item.find('a').attrs['href']) else: pass return links
這裡getLink函式新增一個proxy變數主要是為了後面如果要用ip池的話方便改代理。然後為什麼在選擇網址時加了一個篩選條件呢,主要是因為應屆生網站職位的詳細資訊頁面分為兩種,如果來源是應屆生網,如下
可以看到職位資訊非常的規整,很方便地可以進行資訊提取。而如果來源是一些其他的網站
基本就是一大段描述,而且一家和一家不一樣,所以這裡在爬取職位詳細資訊時,只針對那些來源是應屆生網的網頁。
然後第二步就是資訊提取了,這對大家來說都不是什麼問題了,就是利用BeautifulSoup來提取網頁資訊,網頁原始碼如下
可以看到,公司的資訊都在<div class="main">中,而職位的詳細資訊都在<div class="section">中,結合標籤所處的位置我們可以完成資訊提取,實現程式碼如下:
然後第三步是儲存到本地的資料庫中,這裡使用的是MongoDB,結合pymongo庫幾步就可以實現。然後ip池的程式碼之前也說過了,略有一些小調整如下:def getInfo(url,proxy=''): info={u'公司名稱':'',u'所屬行業':'',u'企業規模':'',u'企業性質':'',u'職位名稱':'',u'工作地點':'',u'有效日期':'',u'招聘人數':'',u'職位性質':'',u'職位描述':''} response=requests.get(url,proxies=proxy) response.encoding='gbk' bs=BeautifulSoup(response.text) info[u'公司名稱']=bs.find('div',{'class':'main'}).find('h1').text temp=[] for item in bs.find('div',{'class':'main'}).find('ul').find_all('li'): temp.append(item.find('span').text) info[u'所屬行業']=temp[0] info[u'企業規模']=temp[1] info[u'企業性質']=temp[2] info[u'職位名稱']=bs.find('div',{'class':'main'}).find('h2').text temp=[] for item in bs.find('div',{'class':'main'}).find('div',{'class':'job_list'}).find('ul').find_all('li'): try: temp.append(item.find('span').text) except: temp.append(item.text) info[u'工作地點']=temp[0] info[u'有效日期']=temp[1] info[u'招聘人數']=temp[2] info[u'職位性質']=re.sub(u'\u804c\u4f4d\u6027\u8d28\uff1a','',temp[3]) describe=bs.find('div',{'class':'main'}).find('div',{'class':'j_i'}).text pattern=pattern=re.compile('\t|\r\n') describe=re.sub(pattern,'',describe) info[u'職位描述']=describe doc.insert_one(info)
#爬取代理ip
def getIp(numpage):
csvfile = file('ips.csv', 'ab')
writer = csv.writer(csvfile)
url='http://www.xicidaili.com/nn/'
user_agent='IP'
headers={'User-agent':user_agent}
for i in xrange(1,numpage+1):
real_url=url+str(i)
response=requests.get(real_url,headers=headers)
content=response.text
bs=BeautifulSoup(content)
trs=bs.find_all('tr')
for items in trs:
tds=items.find_all('td')
temp=[]
try:
temp.append(tds[1].text)
temp.append(tds[2].text)
writer.writerow(temp)
except:
pass
#從本地獲取ip
def getProxy():
reader=csv.reader(open('ips.csv'))
Proxy=[]
for row in reader:
proxy={"http":row[0]+':'+row[1]}
Proxy.append(proxy)
return Proxy
#ip測試
def Test(proxy):
try:
response=requests.get('http://www.yingjiesheng.com/',proxies=proxy,timeout=2)
if response:
return proxy
except:
pass
這樣的話所需要的功能都有了稍微組織一下就能實現我們最終的功能了
if __name__=='__main__':
time1=time.time()
connection=pymongo.MongoClient()
db=connection.YingJieSheng
doc=db.info
getIp(10)
print 'Have got ips , now testing'
proxy=getProxy()
pool=multiprocessing.Pool(processes=16)
IPPool=[]
temp=[]
for item in proxy:
temp.append(pool.apply_async(Test,(item,)))
pool.close()
pool.join()
for item in temp:
IPPool.append(item.get())
print 'Effective ips have been selected'
url_head='http://www.yingjiesheng.com/beijing-morejob-'
url_end='.html'
for i in xrange(1,361):
links=[]
ipproxy=IPPool[i%len(IPPool)-1]
try:
links=getLink(url_head+str(i)+url_end,ipproxy)
except:
links=getLink(url_head+str(i)+url_end)
for link in links:
try:
getInfo(link,ipproxy)
except:
try:
getInfo(link)
except:
pass
print 'Downloading the '+str(i)+' page'
time2=time.time()
print 'Total time is '+str(time2-time1)+' s'
最後執行的結果如下所示:
這樣就完成了我們第一個完整的爬蟲,爬取應屆生網上所有職位的詳細資訊。當然實際操作過程中還是有一點點小問題,那就是很多爬取到ip代理不太好用,導致最後很多網頁還是以本地ip去爬的,訪問頻率過高之後依然會導致ip被封的問題。所以如果不是很著急的話,還是在程式碼里加上sleep,不要對網站的訪問過於密集了,以免造成伺服器不必要的負擔,我覺得這算是爬蟲比較基本的素養吧。
最後,祝大家新年快樂~新的一年裡,希望可以繼續朝著自己心裡的方向前進,加油!!!