一、前言
這次的實(shí)驗(yàn)的任務(wù)是要爬取天天基金網(wǎng)的6000多個(gè)基金,并把爬取的數(shù)據(jù)存放到Mongodb數(shù)據(jù)庫中,數(shù)據(jù)以供下次分析使用。而此次 需要采集的數(shù)據(jù)來自兩個(gè)頁面
-
頁面1:從該頁面爬取 所有基金代碼、基金名稱、基金URL
頁面1 -
頁面2:從上個(gè)頁面獲取的基金URL地址爬取對(duì)應(yīng)基金的近1個(gè)月、近3個(gè)月、近6個(gè)月、近1年、近3年、成立來的變動(dòng)百分比。
圖片.png
二、運(yùn)行環(huán)境
- Python3
- requests
- MongoDb
- bs4
- pymongo
- re
由于python2的字符編碼問題確實(shí)讓人蛋疼,所以今后的試驗(yàn)項(xiàng)目全部改成python3開發(fā),下面會(huì)詳細(xì)介紹蛋疼的原因。
關(guān)于python2和python3字符編碼的問題可以參考以下鏈接:
關(guān)于Python2.X與Python3.X的編碼問題
Python2和Python3之間關(guān)于字符串編碼處理的差別
三、實(shí)例分析
頁面一分析
-
天天基金網(wǎng)這個(gè)頁面所有從0至7開頭的基金代碼分別放在'class="num_box"對(duì)應(yīng)的8個(gè)div中,其中每個(gè)li對(duì)應(yīng)的就是基金所有信息,最后我們用正則表達(dá)式就可以取到我們需要的基金名稱、基金代碼和URL地址。
圖片.png
取所有l(wèi)i基金信息用BeautifulSoup的select方法:
select('.num_right > li')
- 用循環(huán)方法取每個(gè)基金信息,并配合正則表達(dá)式,就可以得到我們需要的基金名稱、基金代碼和url地址。
for tag in tags:
content=tag.a.text #取第一個(gè)<a>的文本數(shù)據(jù)
code=re.findall(r'\d+',content)[0]?。d+從文本數(shù)據(jù)里取數(shù)字,位數(shù)至少大于等于1位,正則表達(dá)式取得的結(jié)果用列表,所以后面用[0]取出數(shù)據(jù)
name=content.split(')')[1] #用中文')'分割取第二個(gè)值得到基金名稱
- 七個(gè).num中的最后一個(gè)<li>里面的內(nèi)容為空值,需要在此做判斷,否則會(huì)提示:not of index
if tag.a is None:
contine?。H绻麨榭罩?,跳過
else:
-
兩個(gè)頁面分別用了兩種編碼方式,第一個(gè)頁面是gb2312,第二個(gè)頁面是utf-8,所以分別定義了2個(gè)不同編碼函數(shù),供兩個(gè)頁面調(diào)用
圖片.png
html=requests.get(url,headers=header).content.decode('gbk')
#gbk編碼擴(kuò)展了gb2312,還支持中文繁體
html=requests.get(url,headers=header).content.decode('utf-8')
頁面二分析
- 從頁面1傳給頁面2的url地址,url格式如:http://fund.eastmoney.com/000001.html 可以分析得出需要的數(shù)據(jù)放在dd 標(biāo)簽里。
圖片.png
先用BeautifulSoup的select方法搜索到。
再用find_all方法獲取dd標(biāo)簽里的第二個(gè)span標(biāo)簽。
tags=soup.select('dd')
m1=(tags[1].find_all('span')[1].string)
y1=(tags[2].find_all('span')[1].string)
m3=(tags[4].find_all('span')[1].string)
y3=(tags[5].find_all('span')[1].string)
m6=(tags[7].find_all('span')[1].string)
rece=(tags[8].find_all('span')[1].string)
detail={'代碼':code,'名稱':name,'近1月':m1,'近3月':m3,'近6月':m6,'近1年':y1,'近3年':y3,'成立來':rece}
- 但當(dāng)用以上方法獲取信息到基金代碼000009時(shí),又提示錯(cuò)誤“IndexError: list index out of range”,經(jīng)分析從頁面1獲取的url地址在頁面2生成的頁面有2種布局方式。
于是再寫一個(gè)函數(shù)獲取第二種布局方式
tags=soup.find_all(class_='ui-font-middle ui-color-red ui-num')
m1=tags[3].string
y1=tags[4].string
m3=tags[5].string
y3=tags[6].string
m6=tags[7].string
rece=tags[8].string
detail={'代碼':code,'名稱':name,'近1月':m1,'近3月':m3,'近6月':m6,'近1年':y1,'近3年':y3,'成立來':rece}
在第一個(gè)方法里加入try...except... 捕捉錯(cuò)誤,當(dāng)遇到錯(cuò)誤時(shí)運(yùn)行第二個(gè)函數(shù)
- 把requests和BeautifulSoup單獨(dú)寫成一個(gè)模塊,以便給其他函數(shù)共用。
from bs4 import BeautifulSoup
import requests,random
def geturl_gbk(url):
html=requests.get(url,headers=header).content.decode('gbk')
soup=BeautifulSoup(html,'lxml')
return soup
def geturl_utf8(url):
html=requests.get(url,headers=header).content.decode('utf-8')
soup=BeautifulSoup(html,'lxml')
return soup
導(dǎo)入MongoDb數(shù)據(jù)庫
import pymongo
clients=pymongo.MongoClient('127.0.0.1')
#建立鏈接
db=clients['hexun']
#指定數(shù)據(jù)庫
col1=db['fund']
#返回?cái)?shù)據(jù)集合1
col2=db['detail']
#返回?cái)?shù)據(jù)集合2
四、實(shí)戰(zhàn)代碼
代碼貼圖:
getstart模塊



完整代碼在github:
On GitHub : Click Here-> 爬取天天基金網(wǎng)代碼
五、MongoDb數(shù)據(jù)截圖


六、總結(jié)
- requests.content和requests.text的方法.content返回的是二進(jìn)制內(nèi)容要用decode指定編碼;text根據(jù)網(wǎng)頁編碼響應(yīng)內(nèi)容來猜測編碼,但此處依舊要指定編碼.
requests.content方法:
html=requests.get(url).content.decode('gbk')
print (html)
request.text方法
html=requests.get(url)
html.encoding='gbk'
print (html.text)
- 此網(wǎng)站會(huì)判斷爬蟲,斷開連接,如下提示:
("Connection broken: ConnectionResetError(104, 'Connection reset by peer')", ConnectionResetError(104, 'Connection reset by peer'))
所以加上了隨機(jī)代理
proxies=['http://118.178.124.33:3128',
'http://139.129.166.68:3128',
'http://61.163.39.70:9999',
'http://61.143.228.162']
html=requests.get(url,headers=header,proxies={'http':random.choice(proxies)}).content.decode('gbk')




