本人是一名java程序員。最近公司須要創建一個車輛的基礎信息庫。須要去網絡上爬取相關的數據。以前都是使用java來編寫爬蟲。一直以來呢都想學習一下python的相關知識,拓寬本身的知識面。以前對python算是零基礎,正好有一個項目需求,趁這個機會呢,學習一下python的相關知識。因此使用了python來編寫這個爬蟲。因此這篇文章屬於我在使用python編寫爬蟲的一份學習筆記。記錄一下在使用python時遇到的一些難題和疑惑內容可能會有錯誤,編寫的代碼上規範也不足。但願之後能慢慢的長進。html
IDE:idea。因爲是個java程序員,因此編譯器也就沒有下載PyCharm。據說很好用。可是人比較懶,仍是之後idea實在用不順手再專門換吧java
python版本:2.7(爲何不用python3?我也不知道python版本2,3之間具體的區別。可是想着java的版本更新到12了。你們用的最廣泛的仍是java8,就選個老版本的下載了。不少疑惑留着之後慢慢解決吧,畢竟是個項目需求,先讓項目跑起來再說hhh)python
前情提要完畢,接下來開始擼代碼git
首先我爬的是汽車之家的信息。一共分三步程序員
import requests
from bs4 import BeautifulSoup
複製代碼
主要使用了兩個模塊,使用requests能夠模擬瀏覽器的請求,相似於java裏的httpclient。Beautiful Soup 是一個能夠從 HTML 或 XML 文件中提取數據的 Python 庫能夠用來解析咱們爬取到的網頁信息。 python裏安裝相關模塊很是的簡單。在控制檯輸入pip install ***相關模塊就能夠安裝使用了。 在 pypi.org/project能夠搜索你想要的安裝的模塊得到對應的命令github
下面是我爬取汽車之家網頁信息的開始部分代碼,也就是請求的部分。一共三個網址,分別第一個url是獲取車輛品牌信息,這個網頁有全部的車輛品牌信息,第二個typeUrl是每一個品牌具體的型號信息。第三個messageUrl是每款型號的具體配置信息。有了這三個網址,其實你們就能夠用本身熟悉的語言去爬取咱們想要的全部的車輛信息了(其實沒有全部的,好比車輛vin碼信息。正是爲了這個信息,我後面放棄了爬取汽車之家的信息,又去爬取了工信部的車輛信息。那個爬取的難度更加的大。)chrome
'''爬取汽車之家車輛信息'''
url = 'https://car.m.autohome.com.cn/' # 獲取車輛品牌信息
typeUrl = 'https://m.autohome.com.cn/' # 車輛型號信息
messageUrl = 'https://car.m.autohome.com.cn/ashx/car/GetModelConfig2.ashx?ids=' #車輛具體信息
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36'
}
httpdatasf = codecs.open(unicode("http代理ip",'utf-8'),"r",encoding='utf-8') #設置文件對象
httpdatas = httpdatasf.readlines() #直接將文件中按行讀到list裏,效果與方法2同樣
httpdatasf.close()
httpsdatasf = codecs.open(unicode("https代理ip",'utf-8'),"r",encoding='utf-8') #設置文件對象
httpsdatas = httpsdatasf.readlines() #直接將文件中按行讀到list裏,效果與方法2同樣
httpsdatasf.close()
requests.adapters.DEFAULT_RETRIES = 8
s = requests.session()
s.keep_alive = False #設置不保持鏈接,不然會未關閉的鏈接太多報錯
s.proxies = {"https": random.choice(httpsdatas) , "http": random.choice(httpdatas) }#代理ip 獲取網址http://www.zdaye.com/FreeIPlist.html
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')
複製代碼
http代理和https代理是我在網上找到的一些免費的代理ip(以下圖)。由於爬蟲爬網站信息畢竟是不少網站都不喜歡的事情。爲了防止IP被封,咱們須要設置一些代理IP去發送請求。經過request獲取session,而後在proxies裏設置好http和https的代理就行了。IP代理獲取地址能夠在www.zdaye.com/FreeIPlist.… 這個網站找到。每一個IP存活的時間有長有短。爲了防止你在爬蟲的時候時不時IP就不可用了,優先採用那些存活時間久一點的IP。對請求加入header,可讓咱們的請求看起來更像是真實的網絡請求。設置keep——alive=False這個比較重要,若是不設置的話,打開太多連接後就會報Max retries exceeded with url的錯誤。而後咱們使用BeautifulSoup就能夠獲取到請求所返回的網頁對象了。json
下面這段代碼就是正式的爬取汽車之家車輛具體信息的三部曲了。首先咱們將request返回信息用BeautifulSoup包裝起來。BeautifulSoup的用法網上有不少例子,這裏我不作具體一一表述。自己也就是一份學習筆記,也沒有必要把用到的全部的東西一一清楚介紹。瀏覽器
咱們所要爬取的車輛品牌信息在id名爲‘div_ListBrand’的標籤中。其中find方法查找的是一個元素,find_all方法查找的是一個結果集。因此要for循環每個li標籤就是每個品牌名稱的一個id號了。獲取到這個li標籤須要獲取li標籤裏的值。這裏id存在了‘v’元素對應的值裏因此去li['v']就獲取了id。若是你要獲取li的value對應的值就是li['value'],以此類推。安全
咱們獲取了每一個品牌對應的id後,須要查找這個品牌下的車型url2(額,這個網址沒定義到全局裏,因爲趕進度,因此這個代碼裏可能有不少不規範的地方,命名啊,之類的。見諒,之後有空再改吧hh)這個請求呢返回的不是一個網頁。而是一串json字符串。因此咱們用了get請求,將response轉出json進行處理。python獲取json字符串裏的值得方法跟以前獲取網頁標籤元素的值相似。
以後咱們將爬取到的車輛型號信息寫入文件中,經過open方法能夠建立文件,w表示寫入,具體還有追加,讀等等操做能夠百度或者谷歌。這裏作點補充,open方法只能open英文名的文件。(不知道是否是啊,反正我打開中文名的文件是失敗了的)因此以前我打開個人ip代理地址文件時,採用的是codecs模塊的open方法。
codecs.open(unicode("https代理ip",'utf-8'),"r",encoding='utf-8')
複製代碼
像這樣加個encoding='utf-8'便可。python的編碼問題還挺多的。不少時候咱們爬取到網頁的請求得到的字符串是unicode編碼的。好比下面代碼獲取的json字符串,咱們直接輸出到文件中就是unicode編碼。json格式的處理方式是
json.dumps(param,ensure_ascii=False)
複製代碼
加入ensure_ascii=False,便可正常輸出中文而不是Unicode編碼了
接下來的操做跟以前的也同樣,咱們將車輛型號id加到typeUrl後,獲取車輛型號信息。最後呢因爲每款型號又具體再有細分。原本我也是像請求獲取具體車輛型號的網頁信息而後解析的。當我解析以後發現,我須要的數據所在的div是空的。這是因爲這些數據都是動態渲染上去的。在此,咱們有兩種解決辦法,一個是此次使用的辦法,打開chrome控制檯,打開network標籤,找到對應獲取車輛型號具體數據的請求。在這裏我找到了獲取後臺數據的請求就是messageUrl了。而後一樣採用get請求的方式獲得json字符串。而後對字符串進行對應的處理
soup = BeautifulSoup(res.text, 'html.parser')
tags = soup.find('div',{'id':'div_ListBrand'})
File = open("vehicle.txt", "w")
ullist = tags.find_all('ul')
for link in ullist:
lilist = link.find_all('li')
for li in lilist:
url2 ='https://car.m.autohome.com.cn/ashx/GetSeriesByBrandId.ashx?b=' + li['v']
response = requests.request("GET", url2, headers=headers)
data = response.json()
ary = data['result']
arry = ary['sellSeries']
for sell in arry:
File.write(str(sell['Id']) +' '+str(sell['name']) +'\n')
SeriesItems = sell['SeriesItems']
for SeriesItem in SeriesItems:
File.write(str(SeriesItem['id'])+' '+str(SeriesItem['name']) +'\n')
typeRes = requests.get(typeUrl+str(SeriesItem['id']), headers=headers)
typeSoup = BeautifulSoup(typeRes.text, 'html.parser')
typeTags = typeSoup.find('div',{'class':'summary-cartype'})
typeDivs = typeTags.find_all('div',{'class':'fn-hide'})
for typeDiv in typeDivs:
typeUls = typeDiv.find_all('ul')
for typeUl in typeUls:
typeLis = typeUl.find_all('li')
for typeLi in typeLis:
print messageUrl+str(typeLi['data-modelid'])
print typeLi.find('a',{'class':'caption'}).get_text()
File.write(typeLi.find('a',{'class':'caption'}).get_text()+'\n')
messageResponse = requests.request("GET", messageUrl+str(typeLi['data-modelid']), headers=headers)
messageData = messageResponse.json()
baseData = messageData['data']
baseJson = json.loads(baseData)
baikeconfigpar = baseJson['baikeconfigpar']
config = baseJson['config']
configbag = baseJson['configbag']
param = baseJson['param']
search = baseJson['search']
File.write(json.dumps(baikeconfigpar,ensure_ascii=False)+'\n')
File.write(json.dumps(config,ensure_ascii=False)+'\n')
File.write(json.dumps(param,ensure_ascii=False)+'\n')
File.write(json.dumps(configbag,ensure_ascii=False)+'\n')
File.write(json.dumps(search,ensure_ascii=False)+'\n')
File.close()
複製代碼
最後咱們獲得了車輛信息的具體信息,主要的參數放在了paramItems裏。這裏有些比較操蛋的是,這個json字符串裏有些數據竟然是直接帶js樣式的好比這個 "value": "<span class='hs_kw0_configpl'></span>A3 2019款 Spo**rtback 35 TFSI 進取型 <span class='hs_kw1_configpl'></span>
後來因爲汽車之家沒有我想要的車輛vin碼信息。我轉而爬取了工信部的車輛信息。因此這方面對json數據的處理我也沒有花精力去作。若是想要獲得乾淨的數據。咱們還能夠採起另外一種方法,直接獲得網頁動態渲染後的數據。這也是我在爬取工信部車輛數據所採用的方法。就是利用了selenium模塊來模擬真實的瀏覽器請求。同時工信部獲取車輛具體數據的接口還須要過一個滑動驗證碼的驗證,一樣能夠利用selenium模塊進行破解。關於這一部分的記錄,等我有空了下回再寫吧,由於還有一些問題沒有處理完,一個就是python的多線程追加寫入文件,如何保證寫入的數據的線程安全。不過相關代碼已上傳到github上:github.com/chenyuhua32…
最後,學習一門新的語言仍是讓我很是的有探索的快樂。但願不斷的記錄下我學習中遇到的坑,讓本身不斷地更好的成長吧。!