携程旅游网与马蜂窝游客记录爬取
前言:這是我學(xué)習(xí)Python爬蟲以來,第一次使用python進(jìn)行大規(guī)模的進(jìn)行數(shù)據(jù)挖掘。邀請我加入她們科研項目的是工商學(xué)院的彭老師,做一個關(guān)于避暑旅游的課題。當(dāng)他們需要獲取攜程旅游筆記時,由于文章的內(nèi)容太多,思路也沒有考慮好,無法使用數(shù)據(jù)采集器把筆記放入Excel。于是找到了我們信息學(xué)院尋求方法幫助,我的輔導(dǎo)員老師就向她推薦了我。終于有機(jī)會進(jìn)行大型的項目實(shí)戰(zhàn)了,非常幸運(yùn)能參與這次的項目。但也由于平時比較繁忙,真正開發(fā)的時間并不是很多,通常都是課余時間做的。
第一次項目交接
她安排她的學(xué)生黃超來與我進(jìn)行交接,告訴我,他們的需求。以前數(shù)據(jù)的采集就是他負(fù)責(zé)的,所以需求還是非常清楚的。他打電話與我溝通,說需要把旅游的筆記放進(jìn)Excel做數(shù)據(jù)分析,我不是很理解,對于這些不規(guī)則的文本,最好的方式應(yīng)該是放進(jìn)txt文本才方便呀,經(jīng)過一些溝通后,確定第二天見面談。我需要在周末上雙學(xué)位的課程,10月13號下午下課后就在我們上課的教室討論,指出我需要爬取的內(nèi)容。
當(dāng)天也相當(dāng)于交流學(xué)習(xí),我從未做過這方面的實(shí)戰(zhàn)。聽他的要求后,最理想的方式是把它文字保存在txt文本,并不是Excel,初步達(dá)成共識。
第一次爬取:攜程網(wǎng),游客游記,關(guān)鍵詞“避暑旅游”,不限時間,初次搜索出4398條
由于數(shù)量太大,更改關(guān)鍵詞為“避暑”,時間為1年內(nèi),游記,共找到800篇,作為這次爬取的目標(biāo)。由于是初次做這樣的工作,變弄便溝通,花了3多小時的時間才完成初步的工作,實(shí)現(xiàn)思路如下:
實(shí)現(xiàn)思路第一步:爬取一篇文本做測試
1.1.1、獲取單篇文章游客筆記文本
通過分析HTML發(fā)現(xiàn),我所需要獲取的文字都在class="ctd_content"這個標(biāo)簽類,直接通過BeautifulSoup的find模塊找到所有的文本就可以了;
運(yùn)行結(jié)果如下:
1.1.2、保存獲取的內(nèi)容到txt文本
txt = str(txt)#把獲取的內(nèi)容轉(zhuǎn)換為字符,建議使用 filename = 'D:write_data.txt' with open(filename,'w',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(txt)f.close()txt截屏:
經(jīng)驗(yàn):為什么要把獲取到的內(nèi)容轉(zhuǎn)化為字符串?
我在寫入txt的過程中,發(fā)現(xiàn)很多文本會提示類型有有誤,轉(zhuǎn)成字符串后就沒有報錯了。
現(xiàn)在算是成功最主要的一步了,接下來就是爬取所有的文章了。
實(shí)現(xiàn)思路第二步:獲取所有文本鏈接
1.2.1、獲取一個主頁的所有文章鏈接
import requests from lxml import etree url = 'https://you.ctrip.com/searchsite/travels/?query=避暑&isAnswered=&isRecommended=&publishDate=365&PageNo=1' html = requests.get(url).text txt = etree.HTML(html) file = txt.xpath('/html/body/div[2]/div[2]/div[2]/div/div[1]/ul/li') for t in file:href = t.xpath('./dl/dt/a/@href')[0]hrefUrl = 'https://you.ctrip.com'+href#把鏈接補(bǔ)充完整print (hrefUrl)獲取結(jié)果:
注意: 攜程網(wǎng)使用的是相對路徑,我們需要把獲取的鏈接補(bǔ)充完整,在前面加上“https://you.ctrip.com”,
1.2.1、獲取每個主頁的鏈接
1.2.1、獲取每個主頁的所有文章鏈接,并添加到數(shù)組
為什么要把獲取到的鏈接存在數(shù)組,不是直接進(jìn)行獲取它的內(nèi)容?
1、從數(shù)組內(nèi)遍歷出來速度比單獨(dú)爬取一個會更快
2、減少被反爬的概率,經(jīng)常訪問更容易被識別出爬蟲
3、優(yōu)化邏輯
1.2.1、開始獲取所有的文章
import requests from lxml import etree from bs4 import BeautifulSoupheaders = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } k = 0 allUrl = [] for num in range(1,81,1):print(num)url = 'https://you.ctrip.com/searchsite/travels/?query=避暑&isAnswered=&isRecommended=&publishDate=365&PageNo='+str(num)html = requests.get(url).texttxt = etree.HTML(html)file = txt.xpath('/html/body/div[2]/div[2]/div[2]/div/div[1]/ul/li')for t in file:href = t.xpath('./dl/dt/a/@href')[0]hrefUrl = 'https://you.ctrip.com'+hrefallUrl.append(hrefUrl)for useUrl in allUrl:k = k + 1print ("正在獲取第%s篇"%k)print (useUrl)html = requests.get(url = useUrl,headers=headers).textsoup = BeautifulSoup(html, "html.parser")t = soup.find(attrs={"class": "ctd_content"})txt = t.get_text().replace("\n","")txt = str(txt)filename = 'G:write_data.txt'with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(txt)f.close() print ("獲取完畢!")幸運(yùn)點(diǎn):
1、還好這次的運(yùn)氣比較好,沒有遇見服務(wù)器請求失敗的情況,一氣呵成
2、網(wǎng)頁結(jié)構(gòu)統(tǒng)一,沒有出現(xiàn)結(jié)構(gòu)異常而報錯
更具他留下來的要求,還需要爬取攜程網(wǎng)的問答模塊,馬蜂窩的三個模塊,分別是關(guān)鍵詞為“避暑”的游記,攻略和問答。
第二天,我在上課的時間完成了對攜程網(wǎng)問答模塊的爬取,并把完成的文本發(fā)給他先做研究。晚上的時候,他告訴我說,他們老師想見我,和我仔細(xì)的交流一下。
第二次項目交接
周二,我白天要上班到16:30,我晚上還要上三節(jié)課,通過電話約定16:40去找她,簡單的交流一下。這次說是項目交接,還不如說叫做聊天吧,彭老師告訴我她們在做什么項目,并且完成的進(jìn)度,對技術(shù)人員的需求。接下來我就是向她展示了一下我爬取數(shù)據(jù)的思路,方便她在寫文稿的時候有相關(guān)的思路,并且簡單的確認(rèn)了一下接下來數(shù)據(jù)的獲取。
在她看來,會一門別人不會的技術(shù),在做學(xué)術(shù)上是有很大優(yōu)勢的。而我所掌握的網(wǎng)絡(luò)爬蟲就是一門不錯的技術(shù),希望我能做好這門技術(shù),給講了很多考研和做學(xué)術(shù)的事。多參與這類項目的開發(fā),并發(fā)表相關(guān)的論文,對我考研是有很大幫助的,但對于考研的事情,我并沒有明確自己的目標(biāo)。
經(jīng)過交談,她也想學(xué)習(xí)Python爬蟲,并讓我有時間的時候指導(dǎo)一下學(xué)習(xí),后來幫她安裝好了學(xué)習(xí)Python的編譯,給了一些入門基礎(chǔ)教程,讓她先了解一下基礎(chǔ),以后再教授爬蟲實(shí)戰(zhàn)和對付反爬等相關(guān)工作。
當(dāng)天晚上,我開始對馬蜂窩的數(shù)據(jù)進(jìn)行了爬取實(shí)戰(zhàn)。
最近有人使用數(shù)據(jù)采集器爬了馬蜂窩的1800萬數(shù)據(jù)就刷爆了網(wǎng)絡(luò),驚動了互聯(lián)網(wǎng)界和投資界,背后的數(shù)據(jù)團(tuán)隊也因此爆紅。所以它的數(shù)據(jù)又重新做好保護(hù)措施,我這次爬取它還是花了一些時間來研究它的。請求到它的源碼后,發(fā)現(xiàn)我需要的數(shù)據(jù)都沒有在其中,這就有點(diǎn)麻煩了,只能使用其他的辦法了。經(jīng)過仔細(xì)的查看后,我發(fā)現(xiàn)它的數(shù)據(jù)全部都是動態(tài)加載的,你看到哪里,它就加載到哪里,建立這個思維后,我開始使用selenium搞自動化,但是這個的效率就要慢很多了,還好不負(fù)努力,終于把它解決了,花了一節(jié)課的時間運(yùn)行代碼,爬取了所有的游記,共計46篇,速度已經(jīng)慢得難受了,還好是上課時間,聽了一節(jié)課,不影響我時間的使用。
2.1.1、獲取主頁的所有文章鏈接,保存到數(shù)組
import requests from lxml import etree from bs4 import BeautifulSoup filename = 'G:馬蜂窩游記筆記文本.txt' noteBook = 'G:馬蜂窩游記筆記評論.txt'url = 'http://www.mafengwo.cn/search/q.php?q=%E9%81%BF%E6%9A%91&t=notes&seid=D8276190-E622-4B11-A1B6-DF09CF22DD76&mxid=&mid=&mname=&kt=1' headers = {'Referer': 'http://www.mafengwo.cn/search/q.php?q=%E9%81%BF%E6%9A%91&t=notes&seid=D8276190-E622-4B11-A1B6-DF09CF22DD76&mxid=&mid=&mname=&kt=1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } allUrl = [] htmlUrl = requests.get(url,headers = headers).text txtUrl = etree.HTML(htmlUrl) file = txtUrl.xpath('//*[@id="_j_search_result_left"]/div/div/ul/li') for t in file:hrefUrl = t.xpath('./div/div[2]/h3/a/@href')[0]allUrl.append(hrefUrl)print (hrefUrl) print(len(allUrl))部分截屏,一共獲取46條鏈接
2.1.2、打開文章,下拉加載數(shù)據(jù)
使用selenium對js進(jìn)行操作
for t in range(1000,310000,1000):#第一次下拉1000像素,第二次要大于1000,相當(dāng)于從零算起,310000是它的總像素time.sleep(0.1)#延時,模擬人為加載js=f"document.documentElement.scrollTop={t}"#下拉加載driver.execute_script(js)2.1.3、加載數(shù)據(jù)完畢,獲取HTML,并關(guān)閉瀏覽器
由于頁面太多,使用瀏覽器的頁面后要關(guān)閉瀏覽器,減少電腦CPU的消耗,也是桌面整潔
source = driver.page_source#獲取源碼driver.close()#關(guān)閉瀏覽器2.1.4、提取作者筆記和評論
# 提取作者筆記soup = BeautifulSoup(source, "html.parser")note = soup.find(attrs={"class": "_j_content_box"}).get_text()note = note.replace("\n","")note = str(note) #提取筆記評論for usrAnswer in soup.find_all(attrs={"class": "mfw-cmt _j_reply_item"}):answer = usrAnswer.find(attrs={"class": "_j_reply_content"}).get_text()answer = str(answer)2.1.5、分開保存筆記和評論
分開保存的目的主要是為了方便研究,她們并沒有提要求,到這樣做比較保險。
# 保存作者筆記with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(note+"\n")f.close() #保存筆記評論with open(noteBook,'a',encoding='utf-8') as g: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!g.write(answer+"\n")g.close()2.1.6、大功告成,開始批量爬取
from selenium import webdriver import time import requests from lxml import etree from bs4 import BeautifulSoupfilename = 'G:馬蜂窩游記筆記文本.txt' noteBook = 'G:馬蜂窩游記筆記評論.txt' url = 'http://www.mafengwo.cn/search/q.php?q=%E9%81%BF%E6%9A%91&t=notes&seid=D8276190-E622-4B11-A1B6-DF09CF22DD76&mxid=&mid=&mname=&kt=1' headers = {'Referer': 'http://www.mafengwo.cn/search/q.php?q=%E9%81%BF%E6%9A%91&t=notes&seid=D8276190-E622-4B11-A1B6-DF09CF22DD76&mxid=&mid=&mname=&kt=1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } allUrl = [] htmlUrl = requests.get(url,headers = headers).text txtUrl = etree.HTML(htmlUrl) file = txtUrl.xpath('//*[@id="_j_search_result_left"]/div/div/ul/li') for t in file:hrefUrl = t.xpath('./div/div[2]/h3/a/@href')[0]allUrl.append(hrefUrl) for urlUser in allUrl:print("正在爬取:",urlUser)driver = webdriver.Chrome('D:\\Software\\chromedriver.exe')driver.get(urlUser)#打開馬蜂窩driver.implicitly_wait(6)#等待加載六秒time.sleep(6)for t in range(1000,310000,1000):#第一次下拉1000像素,第二次要大于1000,相當(dāng)于從零算起,310000是它的總像素time.sleep(0.1)#延時,模擬人為加載js=f"document.documentElement.scrollTop={t}"#下拉加載driver.execute_script(js)source = driver.page_source#獲取源碼driver.close()#關(guān)閉瀏覽器# 提取作者筆記soup = BeautifulSoup(source, "html.parser")note = soup.find(attrs={"class": "_j_content_box"}).get_text()note = note.replace("\n","")note = str(note)# 保存作者筆記with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(note+"\n")f.close()#提取筆記評論for usrAnswer in soup.find_all(attrs={"class": "mfw-cmt _j_reply_item"}):answer = usrAnswer.find(attrs={"class": "_j_reply_content"}).get_text()answer = str(answer)#保存筆記評論with open(noteBook,'a',encoding='utf-8') as g: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!g.write(answer+"\n")g.close()print ("該頁評論已下載完畢!")終于解決了,看以來還行~
2.1.7、更具這樣的思路,完成了馬蜂窩攻略和問答的爬取
馬蜂窩攻略和問答與游記不同的地方在于,它倆的文章比較多,需要翻頁,這樣使用一個循環(huán)就構(gòu)造好它的鏈接了,還是比較容易的,圓滿完成任務(wù)!
第三次項目交接
這次黃超同學(xué)重新更改了他的需求,需要重新爬取攜程網(wǎng)游記,從以前的一年之內(nèi)改為時間不限,從800篇變到了9331篇
這樣的任務(wù)量無疑增加得大了,如果是按照先前的方式運(yùn)行代碼一篇一篇的爬取,在運(yùn)行順利的情況下,估計也要花兩三個小時吧。所以必須解決速度問題… …
方法一——使用多進(jìn)程:
我先考慮使用多進(jìn)程了完成這項工作,分別讓它往一個文本中寫入數(shù)據(jù),多開幾個進(jìn)程。考慮到請求數(shù)據(jù)庫失敗,代碼報錯的問題,我選擇使用try函數(shù)跳過錯誤的請求失敗的文章,執(zhí)行后我發(fā)現(xiàn)被跳過的文章太多,如果要重新爬取這些文章或減少損失,這是一件不容易的事情。
方法二——分為10段,同時開10個程序:
我需要爬取的文章從1~9331,被我分為了10段執(zhí)行,并分別把文本寫入10個txt文件,哪一個錯就重新執(zhí)行哪一個,10個代碼同時執(zhí)行。
第一個程序:第1~101篇,文件夾:filename = 'G:攜程游記之避暑01.txt' 第二個程序:第1001~201篇文件夾:filename = 'G:攜程游記之避暑02.txt' 第三個程序:第301~401篇文件夾:filename = 'G:攜程游記之避暑03.txt' ......... 第三個程序:第901~9332篇文件夾:filename = 'G:攜程游記之避暑10.txt'10個程序分開同時執(zhí)行:
保存結(jié)果:
攜程問答爬取:
代碼片段其一:
import requests from bs4 import BeautifulSoup from lxml import etree import time start = time.time()#記錄開始時的時間 filename = "G:攜程問答之避暑3(1).txt"#保存的文本命名及路徑 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } allUrl = []#保存獲取的鏈接 for page in range(1,26,1):index = 'https://you.ctrip.com/searchsite/asks/?query=避暑&isAnswered=&isRecommended=&publishDate=&PageNo=%s'%pageprint ("正在獲取:",index)indexHtml = requests.get(url=index,headers=headers).textetreeIndex = etree.HTML(indexHtml)liUrls = etreeIndex.xpath('/html/body/div[2]/div[2]/div[2]/div/div[1]/ul/li')for li in liUrls:href = li.xpath('./dl/dt/a/@href')[0]hrefUrl = 'https://you.ctrip.com'+hrefallUrl.append(hrefUrl) urlNum = len(allUrl) print ("一共找到%s篇文章需要爬取"%urlNum) c = 0 for textUrl in allUrl:c = c + 1#記錄文章篇數(shù)if c in range(0,4676,10):time.sleep(50)#模擬暫停print ("正在爬取第%s篇文章,一共有4676篇"%c)textHtml = requests.get(url = textUrl,headers = headers).texttextsoup = BeautifulSoup(textHtml,"html.parser")#提取作者筆記標(biāo)題textTitle = textsoup.find(attrs={"class":"ask_title"}).get_text()print (textTitle)textTitle = str(textTitle)with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(textTitle)f.close()#提取作者筆記titleQuestion = textsoup.find(attrs={"class":"ask_text"}).get_text()titleQuestion = str(titleQuestion)with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(titleQuestion)f.close()#提取評論answerBoxs = textsoup.find_all(attrs={"class":"answer_box cf"})for userAnswer in answerBoxs:answer = userAnswer.find(attrs={"class":"answer_text"}).get_text()answer = str(answer)with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(answer+"\n")f.close() end = time.time()#獲取當(dāng)前時間 useTime = int(end-start) useTime = useTime/60 print ("本次爬取所有文章一共使用%s分鐘"%useTime)6個程序同時進(jìn)行:
代碼運(yùn)行結(jié)果:
文本保存結(jié)果:
第四次項目交接
我看見彭老師在群里給她的學(xué)生說,文本有空格和換行會影響數(shù)據(jù)分析,此刻她正在一個一個的刪除文本空格和換行。為了節(jié)約時間,我就告訴她她文本放入word使用替換功能去掉多余的空格和回車。
但是9千多篇文章還是太多了,她們的分析工具把電腦弄死機(jī)幾次后,重新決定只要三年的數(shù)據(jù),但是攜程網(wǎng)上又沒有專門篩選三年數(shù)據(jù)的地方,擔(dān)心不能實(shí)現(xiàn)。
后來我通過提取每個游客的發(fā)表時間來提取文章,如果滿足2017,2018,2019年,就把它的鏈接追加在保存鏈接的數(shù)組中,達(dá)篩到的目的。
第一頁,1~101頁查找結(jié)果:
源碼如下:
第五次項目交接
三年的數(shù)據(jù)這樣存放不好使用分析,文件還是太大了,需要把2017,2018,2019年的數(shù)據(jù)單獨(dú)存放,于是又對代碼做出了一些更改,完成了最后一次需求!
import requests,time from lxml import etree from bs4 import BeautifulSoup start = time.time() filename = 'G:攜程游記之避暑201910.txt' headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } allUrl = [] c = 0 for page in range(901,932,1):#1,932,1indexUrl = 'https://you.ctrip.com/searchsite/travels/?query=避暑&isAnswered=&isRecommended=&publishDate=&PageNo=%s'%pageprint ("正在獲取:",indexUrl)indexHtml = requests.get(url=indexUrl,headers=headers).textetreeIndex = etree.HTML(indexHtml)ettimes = etreeIndex.xpath('/html/body/div[2]/div[2]/div[2]/div/div[1]/ul/li')for ettime in ettimes:try:pTime = ettime.xpath('./dl/dd[2]/text()')[1]pTime = pTime.split("發(fā)表于")[1]pTime = pTime.split("-")[0]href = ettime.xpath('./dl/dt/a/@href')[0]hrefUrl = 'https://you.ctrip.com' + hrefif int(pTime) == 2019:print ("符合條件的時間",pTime)print (hrefUrl)allUrl.append(hrefUrl)except:pass urlNum = len(allUrl) print ("一共找到%s篇文章需要爬取"%urlNum) for textUrl in allUrl:c = c + 1print ("正在爬取第%s篇文章,一共有%s篇"%(c,urlNum))try:html = requests.get(url = textUrl,headers=headers).textsoup = BeautifulSoup(html, "html.parser")try:title = soup.find(attrs={"class": "title1"}).get_text().replace("\n","").replace(" ","")except:title = soup.find(attrs={"class": "ctd_head_left"}).get_text().replace("\n","").replace(" ","")title = str(title)print ("正在爬取:",title)t = soup.find(attrs={"class": "ctd_content"})txt = t.get_text().replace("\n","").replace(" ","")txt = str(txt)with open(filename,'a',encoding='utf-8') as f: # 如果filename不存在會自動創(chuàng)建, 'w'表示寫數(shù)據(jù),寫之前會清空文件中的原有數(shù)據(jù)!f.write(title)f.write(txt)f.close()except:pass end = time.time() useTime = int(end-start) useTime = useTime/60 print ("該次爬取這%s文章共使用%s分鐘"%(urlNum,useTime))運(yùn)行結(jié)果截屏:
保存文本結(jié)果:
很感謝這次的實(shí)戰(zhàn)機(jī)會,通過很多次的需求,讓我學(xué)會了很多處理數(shù)據(jù)的方法,提高自己的編程實(shí)戰(zhàn)能力,幫上一點(diǎn)小忙。
總結(jié)
以上是生活随笔為你收集整理的携程旅游网与马蜂窝游客记录爬取的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析信息化项目的信息化绩效评价
- 下一篇: 计算机考研数据结构答案,计算机考研数据结