方法/步驟
創(chuàng)新互聯(lián)建站,專(zhuān)注為中小企業(yè)提供官網(wǎng)建設(shè)、營(yíng)銷(xiāo)型網(wǎng)站制作、響應(yīng)式網(wǎng)站設(shè)計(jì)、展示型網(wǎng)站建設(shè)、網(wǎng)站制作等服務(wù),幫助中小企業(yè)通過(guò)網(wǎng)站體現(xiàn)價(jià)值、有效益。幫助企業(yè)快速建站、解決網(wǎng)站建設(shè)與網(wǎng)站營(yíng)銷(xiāo)推廣問(wèn)題。
在做爬取數(shù)據(jù)之前,你需要下載安裝兩個(gè)東西,一個(gè)是urllib,另外一個(gè)是python-docx。
請(qǐng)點(diǎn)擊輸入圖片描述
然后在python的編輯器中輸入import選項(xiàng),提供這兩個(gè)庫(kù)的服務(wù)
請(qǐng)點(diǎn)擊輸入圖片描述
urllib主要負(fù)責(zé)抓取網(wǎng)頁(yè)的數(shù)據(jù),單純的抓取網(wǎng)頁(yè)數(shù)據(jù)其實(shí)很簡(jiǎn)單,輸入如圖所示的命令,后面帶鏈接即可。
請(qǐng)點(diǎn)擊輸入圖片描述
抓取下來(lái)了,還不算,必須要進(jìn)行讀取,否則無(wú)效。
請(qǐng)點(diǎn)擊輸入圖片描述
5
接下來(lái)就是抓碼了,不轉(zhuǎn)碼是完成不了保存的,將讀取的函數(shù)read轉(zhuǎn)碼。再隨便標(biāo)記一個(gè)比如XA。
請(qǐng)點(diǎn)擊輸入圖片描述
6
最后再輸入三句,第一句的意思是新建一個(gè)空白的word文檔。
第二句的意思是在文檔中添加正文段落,將變量XA抓取下來(lái)的東西導(dǎo)進(jìn)去。
第三句的意思是保存文檔docx,名字在括號(hào)里面。
請(qǐng)點(diǎn)擊輸入圖片描述
7
這個(gè)爬下來(lái)的是源代碼,如果還需要篩選的話需要自己去添加各種正則表達(dá)式。
關(guān)于知乎驗(yàn)證碼登陸的問(wèn)題,用到了Python上一個(gè)重要的圖片處理庫(kù)PIL,如果不行,就把圖片存到本地,手動(dòng)輸入。
通過(guò)對(duì)知乎登陸是的抓包,可以發(fā)現(xiàn)登陸知乎,需要post三個(gè)參數(shù),一個(gè)是賬號(hào),一個(gè)是密碼,一個(gè)是xrsf。
這個(gè)xrsf隱藏在表單里面,每次登陸的時(shí)候,應(yīng)該是服務(wù)器隨機(jī)產(chǎn)生一個(gè)字符串。所有,要模擬登陸的時(shí)候,必須要拿到xrsf。
用chrome (或者火狐 httpfox 抓包分析)的結(jié)果:
所以,必須要拿到xsrf的數(shù)值,注意這是一個(gè)動(dòng)態(tài)變化的參數(shù),每次都不一樣。
拿到xsrf,下面就可以模擬登陸了。
使用requests庫(kù)的session對(duì)象,建立一個(gè)會(huì)話的好處是,可以把同一個(gè)用戶(hù)的不同請(qǐng)求聯(lián)系起來(lái),直到會(huì)話結(jié)束都會(huì)自動(dòng)處理cookies。
注意:cookies 是當(dāng)前目錄的一個(gè)文件,這個(gè)文件保存了知乎的cookie,如果是第一個(gè)登陸,那么當(dāng)然是沒(méi)有這個(gè)文件的,不能通過(guò)cookie文件來(lái)登陸。必須要輸入密碼。
這是登陸的函數(shù),通過(guò)login函數(shù)來(lái)登陸,post 自己的賬號(hào),密碼和xrsf 到知乎登陸認(rèn)證的頁(yè)面上去,然后得到cookie,將cookie保存到當(dāng)前目錄下的文件里面。下次登陸的時(shí)候,直接讀取這個(gè)cookie文件。
這是cookie文件的內(nèi)容
以下是源碼:
運(yùn)行結(jié)果:
反爬蟲(chóng)最基本的策略:
爬蟲(chóng)策略:
這兩個(gè)都是在http協(xié)議的報(bào)文段的檢查,同樣爬蟲(chóng)端可以很方便的設(shè)置這些字段的值,來(lái)欺騙服務(wù)器。
反爬蟲(chóng)進(jìn)階策略:
1.像知乎一樣,在登錄的表單里面放入一個(gè)隱藏字段,里面會(huì)有一個(gè)隨機(jī)數(shù),每次都不一樣,這樣除非你的爬蟲(chóng)腳本能夠解析這個(gè)隨機(jī)數(shù),否則下次爬的時(shí)候就不行了。
2.記錄訪問(wèn)的ip,統(tǒng)計(jì)訪問(wèn)次數(shù),如果次數(shù)太高,可以認(rèn)為這個(gè)ip有問(wèn)題。
爬蟲(chóng)進(jìn)階策略:
1.像這篇文章提到的,爬蟲(chóng)也可以先解析一下隱藏字段的值,然后再進(jìn)行模擬登錄。
2.爬蟲(chóng)可以使用ip代理池的方式,來(lái)避免被發(fā)現(xiàn)。同時(shí),也可以爬一會(huì)休息一會(huì)的方式來(lái)降低頻率。另外,服務(wù)器根據(jù)ip訪問(wèn)次數(shù)來(lái)進(jìn)行反爬,再ipv6沒(méi)有全面普及的時(shí)代,這個(gè)策略會(huì)很容易造成誤傷。(這個(gè)是我個(gè)人的理解)。
通過(guò)Cookie限制進(jìn)行反爬蟲(chóng):
和Headers校驗(yàn)的反爬蟲(chóng)機(jī)制類(lèi)似,當(dāng)用戶(hù)向目標(biāo)網(wǎng)站發(fā)送請(qǐng)求時(shí),會(huì)再請(qǐng)求數(shù)據(jù)中攜帶Cookie,網(wǎng)站通過(guò)校驗(yàn)請(qǐng)求信息是否存在Cookie,以及校驗(yàn)Cookie的值來(lái)判定發(fā)起訪問(wèn)請(qǐng)求的到底是真實(shí)的用戶(hù)還是爬蟲(chóng),第一次打開(kāi)網(wǎng)頁(yè)會(huì)生成一個(gè)隨機(jī)cookie,如果再次打開(kāi)網(wǎng)頁(yè)這個(gè)Cookie不存在,那么再次設(shè)置,第三次打開(kāi)仍然不存在,這就非常有可能是爬蟲(chóng)在工作了。
反爬蟲(chóng)進(jìn)進(jìn)階策略:
1.數(shù)據(jù)投毒,服務(wù)器在自己的頁(yè)面上放置很多隱藏的url,這些url存在于html文件文件里面,但是通過(guò)css或者js使他們不會(huì)被顯示在用戶(hù)看到的頁(yè)面上面。(確保用戶(hù)點(diǎn)擊不到)。那么,爬蟲(chóng)在爬取網(wǎng)頁(yè)的時(shí)候,很用可能取訪問(wèn)這個(gè)url,服務(wù)器可以100%的認(rèn)為這是爬蟲(chóng)干的,然后可以返回給他一些錯(cuò)誤的數(shù)據(jù),或者是拒絕響應(yīng)。
爬蟲(chóng)進(jìn)進(jìn)階策略:
1.各個(gè)網(wǎng)站雖然需要反爬蟲(chóng),但是不能夠把百度,谷歌這樣的搜索引擎的爬蟲(chóng)給干了(干了的話,你的網(wǎng)站在百度都說(shuō)搜不到?。_@樣爬蟲(chóng)應(yīng)該就可以冒充是百度的爬蟲(chóng)去爬。(但是ip也許可能被識(shí)破,因?yàn)槟愕膇p并不是百度的ip)
反爬蟲(chóng)進(jìn)進(jìn)進(jìn)階策略:
給個(gè)驗(yàn)證碼,讓你輸入以后才能登錄,登錄之后,才能訪問(wèn)。
爬蟲(chóng)進(jìn)進(jìn)進(jìn)階策略:
圖像識(shí)別,機(jī)器學(xué)習(xí),識(shí)別驗(yàn)證碼。不過(guò)這個(gè)應(yīng)該比較難,或者說(shuō)成本比較高。
參考資料:
廖雪峰的python教程
靜覓的python教程
requests庫(kù)官方文檔
segmentfault上面有一個(gè)人的關(guān)于知乎爬蟲(chóng)的博客,找不到鏈接了
本來(lái)是想爬取之后作最佳羈絆組合推算,但是遇到知識(shí)點(diǎn)無(wú)法消化(知識(shí)圖譜),所以暫時(shí)先不組合了,實(shí)力有限
庫(kù)的安裝
1.requests? #爬取棋子數(shù)據(jù)
2.json? #棋子數(shù)據(jù)為js動(dòng)態(tài),需使用json解析
3.BeautifulSoup
實(shí)戰(zhàn)前先新建個(gè)lol文件夾作為工作目錄,并創(chuàng)建子目錄data,用于存放數(shù)據(jù)。
1.爬取數(shù)據(jù),新建個(gè)py文件,用于爬取云頂數(shù)據(jù),命名為data.py
1.1定義個(gè)req函數(shù),方便讀取。//需設(shè)定編碼格式,否則會(huì)出現(xiàn)亂碼
def Re_data(url):
re = requests.get(url)
re.encoding = 'gbk'
data = json.loads(re.text)
return data['data']
1.2定義個(gè)Get函數(shù),用于讀取數(shù)據(jù)并使用保存函數(shù)進(jìn)行保存數(shù)據(jù),保存格式為json。
def Get_data():
# 獲取數(shù)據(jù)并保存至data目錄
base_url = ''
chess = Re_data(base_url + 'chess.js')
race = Re_data(base_url + 'race.js')
job = Re_data(base_url + 'job.js')
equip = Re_data(base_url + 'equip.js')
Save_data(chess,race,job,equip)
1.3定義save函數(shù)實(shí)現(xiàn)讀取的數(shù)據(jù)進(jìn)行文件保存,保存目錄為工作目錄下的data文件夾。
def Save_data(t_chess,t_race,t_job,t_equip):
with open('./data/chess.json','w') as f:
json.dump(t_chess,f,indent='\t')
with open('./data/race.json','w') as f:
json.dump(t_race,f,indent='\t')
with open('./data/job.json','w') as f:
json.dump(t_job,f,indent='\t')
with open('./data/equip.json','w') as f:
json.dump(t_equip,f,indent='\t')
1.4定義主函數(shù)main跑起來(lái)
if __name__ == '__main__':
start = time.time()
Get_data()
print('運(yùn)行時(shí)間:' + str(time.time() - start) + '秒')
至此,數(shù)據(jù)爬取完成。
2.種族和職業(yè)進(jìn)行組合。
2.1未完成 //未完成,使用窮舉方法進(jìn)行組合會(huì)出現(xiàn)內(nèi)存不夠?qū)е陆M合失?。╢or循環(huán)嵌套導(dǎo)致數(shù)組內(nèi)存超限)
//待學(xué)習(xí),使用知識(shí)圖譜建立組合優(yōu)選,可參考:
期間遇到的問(wèn)題:
1.爬取棋子數(shù)據(jù)時(shí)為動(dòng)態(tài)js加載,需通過(guò)json模塊的loads方法獲取
2.3層for循環(huán)嵌套數(shù)據(jù)量大,導(dǎo)致計(jì)算失敗,需優(yōu)化計(jì)算方法。
一、爬取我們所需要的一線鏈接
channel_extract.py
這里的一線鏈接也就是我們所說(shuō)的大類(lèi)鏈接:
from bs4 import BeautifulSoupimport requests
start_url = ''host_url = ''def get_channel_urls(url):
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text, 'lxml')
links = soup.select('.fenlei dt a') #print(links)
for link in links:
page_url = host_url + link.get('href')
print(page_url)#get_channel_urls(start_url)channel_urls = '''
'''123456789101112131415161718192021222324252627282930313233343536
那么拿我爬取的58同城為例就是爬取了二手市場(chǎng)所有品類(lèi)的鏈接,也就是我說(shuō)的大類(lèi)鏈接;
找到這些鏈接的共同特征,用函數(shù)將其輸出,并作為多行文本儲(chǔ)存起來(lái)。
二、獲取我們所需要的詳情頁(yè)面的鏈接和詳情信息
page_parsing.py
1、說(shuō)說(shuō)我們的數(shù)據(jù)庫(kù):
先看代碼:
#引入庫(kù)文件from bs4 import BeautifulSoupimport requestsimport pymongo #python操作MongoDB的庫(kù)import reimport time#鏈接和建立數(shù)據(jù)庫(kù)client = pymongo.MongoClient('localhost', 27017)
ceshi = client['ceshi'] #建ceshi數(shù)據(jù)庫(kù)ganji_url_list = ceshi['ganji_url_list'] #建立表文件ganji_url_info = ceshi['ganji_url_info']123456789101112
2、判斷頁(yè)面結(jié)構(gòu)是否和我們想要的頁(yè)面結(jié)構(gòu)相匹配,比如有時(shí)候會(huì)有404頁(yè)面;
3、從頁(yè)面中提取我們想要的鏈接,也就是每個(gè)詳情頁(yè)面的鏈接;
這里我們要說(shuō)的是一個(gè)方法就是:
item_link = link.get('href').split('?')[0]12
這里的這個(gè)link什么類(lèi)型的,這個(gè)get方法又是什么鬼?
后來(lái)我發(fā)現(xiàn)了這個(gè)類(lèi)型是
class 'bs4.element.Tab1
如果我們想要單獨(dú)獲取某個(gè)屬性,可以這樣,例如我們獲取它的 class 叫什么
print soup.p['class']
#['title']12
還可以這樣,利用get方法,傳入屬性的名稱(chēng),二者是等價(jià)的
print soup.p.get('class')#['title']12
下面我來(lái)貼上代碼:
#爬取所有商品的詳情頁(yè)面鏈接:def get_type_links(channel, num):
list_view = '{0}o{1}/'.format(channel, str(num)) #print(list_view)
wb_data = requests.get(list_view)
soup = BeautifulSoup(wb_data.text, 'lxml')
linkOn = soup.select('.pageBox') #判斷是否為我們所需頁(yè)面的標(biāo)志;
#如果爬下來(lái)的select鏈接為這樣:div.pageBox ul li:nth-child(1) a span 這里的:nth-child(1)要?jiǎng)h掉
#print(linkOn)
if linkOn:
link = soup.select('.zz .zz-til a')
link_2 = soup.select('.js-item a')
link = link + link_2 #print(len(link))
for linkc in link:
linkc = linkc.get('href')
ganji_url_list.insert_one({'url': linkc})
print(linkc) else: pass1234567891011121314151617181920
4、爬取詳情頁(yè)中我們所需要的信息
我來(lái)貼一段代碼:
#爬取趕集網(wǎng)詳情頁(yè)鏈接:def get_url_info_ganji(url):
time.sleep(1)
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text, 'lxml') try:
title = soup.select('head title')[0].text
timec = soup.select('.pr-5')[0].text.strip()
type = soup.select('.det-infor li span a')[0].text
price = soup.select('.det-infor li i')[0].text
place = soup.select('.det-infor li a')[1:]
placeb = [] for placec in place:
placeb.append(placec.text)
tag = soup.select('.second-dt-bewrite ul li')[0].text
tag = ''.join(tag.split()) #print(time.split())
data = { 'url' : url, 'title' : title, 'time' : timec.split(), 'type' : type, 'price' : price, 'place' : placeb, 'new' : tag
}
ganji_url_info.insert_one(data) #向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù);
print(data) except IndexError: pass123456789101112131415161718192021222324252627282930
四、我們的主函數(shù)怎么寫(xiě)?
main.py
看代碼:
#先從別的文件中引入函數(shù)和數(shù)據(jù):from multiprocessing import Poolfrom page_parsing import get_type_links,get_url_info_ganji,ganji_url_listfrom channel_extract import channel_urls#爬取所有鏈接的函數(shù):def get_all_links_from(channel):
for i in range(1,100):
get_type_links(channel,i)#后執(zhí)行這個(gè)函數(shù)用來(lái)爬取所有詳情頁(yè)的文件:if __name__ == '__main__':# pool = Pool()# # pool = Pool()# pool.map(get_url_info_ganji, [url['url'] for url in ganji_url_list.find()])# pool.close()# pool.join()#先執(zhí)行下面的這個(gè)函數(shù),用來(lái)爬取所有的鏈接:if __name__ == '__main__':
pool = Pool()
pool = Pool()
pool.map(get_all_links_from,channel_urls.split())
pool.close()
pool.join()1234567891011121314151617181920212223242526
五、計(jì)數(shù)程序
count.py
用來(lái)顯示爬取數(shù)據(jù)的數(shù)目;
import timefrom page_parsing import ganji_url_list,ganji_url_infowhile True: # print(ganji_url_list.find().count())
# time.sleep(5)
print(ganji_url_info.find().count())
time.sleep(5)
我們?cè)谝恍](méi)有反爬蟲(chóng)機(jī)制的生物網(wǎng)站上,可以利用Python做一些“省力”的事情,比方說(shuō)ID的轉(zhuǎn)換
我們以u(píng)niprot為例,進(jìn)入它的轉(zhuǎn)換頁(yè)面 傳送門(mén) ,頁(yè)面:
這里介紹下 urllib 這個(gè)Python庫(kù),該庫(kù)功能非常強(qiáng)大,可以爬取動(dòng)態(tài)網(wǎng)頁(yè)
根據(jù)這個(gè)思路,我們看看該網(wǎng)站的網(wǎng)頁(yè)結(jié)構(gòu):
url 為ID轉(zhuǎn)換的網(wǎng)址,params是你要爬取的內(nèi)容,存儲(chǔ)為字典形式,那么字典的鍵值代表HTML表單(form)里面 name 的內(nèi)容:
比方說(shuō) from:
字典的鍵值與form的name標(biāo)簽的內(nèi)容對(duì)應(yīng)
to也是一樣的:
至于 "format": "tab" 是指我們把爬下來(lái)的網(wǎng)頁(yè)轉(zhuǎn)換為 tab 格式
當(dāng)然,這招適用于表單提交的爬蟲(chóng),如果爬取的內(nèi)容過(guò)多,不妨寫(xiě)個(gè)函數(shù):
參考: 傳送門(mén)
在if 里只需要yield "" + item_url.attrs['href']
然后Lsit(最好改可名,在python規(guī)范里,函數(shù)命名是全小寫(xiě),而list又是保留字,比如改為display_hrefs)只需要循環(huán)輸出getUrl的結(jié)果就好:
def getUrl(url: str):
....html = urlopen(url)
....for item_url in BeautifulSoup((html.read()).find ('div' , class_='AAA').findAll ("a"):
........if 'href' in item_url.attrs:
............yield "" + item_url.attrs['href']
def display_hrefs(url: str):
....for href in getUrl(url):
........print(href)
if __name__ == '__main__':
....display_hrefs("")