據(jù)觀世界
在大數(shù)據(jù)、人工智能時代,我們通常需要從網(wǎng)站中收集我們所需的數(shù)據(jù),網(wǎng)絡(luò)信息的爬取技術(shù)已經(jīng)成為多個行業(yè)所需的技能之一。而Python則是目前數(shù)據(jù)科學(xué)項目中最常用的編程語言之一。使用Python與BeautifulSoup可以很容易的進行網(wǎng)頁爬取,通過網(wǎng)站爬蟲獲取信息可以幫助企業(yè)或個人節(jié)省很多的時間和金錢。學(xué)習(xí)本文之后,我相信大部分新手都能根據(jù)自己的需求來開發(fā)出相應(yīng)的網(wǎng)頁爬蟲。
如果您是完全的新手也不用擔(dān)心,通過本文您可以很容易地理解。
首先,您需要先安裝好Python 3.x,Python安裝包可以從python.org下載,然后我們需要安裝requests和beautifulsoup4兩個包,安裝代碼如下:
$ pip install requests
$ pip install beautifulsoup4
現(xiàn)在我們已經(jīng)做好了一切準(zhǔn)備工作。在本教程中,我們將演示從沒被墻的維基百科英文版頁面中獲取歷屆美國總統(tǒng)名單。
轉(zhuǎn)到此鏈接(https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States#Presidents)并右鍵單擊包含有關(guān)美國總統(tǒng)的所有信息的表格,然后單擊"檢查"選項(我用的是Chrome瀏覽器,其他瀏覽器右鍵單擊頁面后也會具有類似或相同的選項)。
由下圖可知,表格的內(nèi)容位于class屬性為wikitable的table標(biāo)簽下,我們需要了解這些標(biāo)簽信息來獲取我們所需內(nèi)容。
了解網(wǎng)頁信息之后,我們就可以編寫代碼了。首先,我們要導(dǎo)入我們安裝的包:
import requests
from bs4 import BeautifulSoup
為了獲取網(wǎng)頁數(shù)據(jù)我們要使用requests的get()方法:
url = "https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"
page = requests.get(url)
檢查http響應(yīng)狀態(tài),來確保我們能正常獲取網(wǎng)頁,如果輸出狀態(tài)代碼為200則為正常:
print(page.status_code)
現(xiàn)在我們已經(jīng)獲取了網(wǎng)頁數(shù)據(jù),讓我們看看我們得到了什么:
print(page.content)
上面的代碼會顯示http相應(yīng)的全部內(nèi)容,包括html代碼和我們需要的文本數(shù)據(jù)信息。通過使用beautifulsoup的prettify()方法可以將其更美觀的展示出來:
soup = BeautifulSoup(page.content, 'html.parser')
print(soup.prettify())
這會將數(shù)據(jù)按照我們上面"檢查"中看到的代碼形式展示出來:
目前獲得的是包含我們所需數(shù)據(jù)的網(wǎng)頁源碼,我們接下來要將有用的信息提取出來。上面已經(jīng)知道我們所需的內(nèi)容在class屬性為wikitable的table標(biāo)簽下,因此,接下來我們將使用bs4對象的find方法提取table標(biāo)簽中的數(shù)據(jù),此方法返回bs4對象:
tb = soup.find('table', class_='wikitable')
table標(biāo)簽下有很多嵌套標(biāo)簽,通過網(wǎng)頁檢查中的代碼可以發(fā)現(xiàn),我們最終是需要獲得title元素中的文本數(shù)據(jù),而title元素位于a標(biāo)簽下,a標(biāo)簽位于b標(biāo)簽下,b標(biāo)簽位于table標(biāo)簽下。為了獲取所有我們所需的數(shù)據(jù),我們需要提取table標(biāo)簽下的所有b標(biāo)簽,然后找到b標(biāo)簽下的所有a標(biāo)簽,為此,我們使用find_all方法來迭代獲取所有b標(biāo)簽下的a標(biāo)簽:
for link in tb.find_all('b'):
name = link.find('a')
print(name)
這將獲取所有a標(biāo)簽下的數(shù)據(jù):
可以看出,這并不是我們所要的最終結(jié)果,其中摻雜著html代碼,不用擔(dān)心,我們只需為上面的代碼添加get_text()方法,即可提取出所有a標(biāo)簽下title元素的文本信息,代碼改動如下:
for link in tb.find_all('b'):
name = link.find('a')
print(name.get_text('title'))
最終獲得所有總統(tǒng)的名單如下:
George Washington
John Adams
Thomas Jefferson
James Monroe
...
...
Barack Obama
Donald Trump
將Python代碼合并在一起:
import requests
from bs4 import BeautifulSoup
url = "https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"
page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
tb = soup.find('table', class_='wikitable')
for link in tb.find_all('b'):
name = link.find('a')
print(name.get_text('title'))
僅僅9行代碼,我們就實現(xiàn)了網(wǎng)頁信息的爬取,我相信您從中已經(jīng)學(xué)到了Python、html的基礎(chǔ)知識,并且已經(jīng)懂得了網(wǎng)頁爬蟲的基本原理,如果想更深入的學(xué)習(xí)網(wǎng)頁爬蟲,你可以嘗試對以上代碼進行改進,嘗試爬取更多的網(wǎng)站,歡迎與我留言交流。
crapy無疑是優(yōu)秀的爬蟲框架,它使用的是類似Requests的方式來爬取網(wǎng)頁,也就是說它爬取的靜態(tài)頁面,但是現(xiàn)實卻是大部分的網(wǎng)站都是動態(tài)的,我們看到的正常頁面都是瀏覽器渲染后的結(jié)果,如果直接使用scrapy爬取網(wǎng)頁很可能得不到期望的數(shù)據(jù)。一種想當(dāng)然的方式,就是通過自定義Middleware使用類似selenium這種模擬瀏覽器行為的方式來直接拿到渲染好的html內(nèi)容,這樣當(dāng)然是可行的,scrapy也可以輕松和selenium對接,不足之處是增加了資源的消耗。
還有一種直達本質(zhì)的方式,就是找到數(shù)據(jù)的源頭,有的通過ajax請求獲取數(shù)據(jù)源,有的將數(shù)據(jù)源放到j(luò)s代碼中,通過瀏覽器開發(fā)工具分析交互過程找到數(shù)據(jù)源頭后,如果是ajax請求就直接請求對應(yīng)的接口就可以拿到數(shù)據(jù),本文將介紹數(shù)據(jù)源在js文件中的提取方法。
這里以某文庫搜索結(jié)果為例,如果直接scrapy爬取頁面是看不到下圖中的搜索結(jié)果的,分析頁面結(jié)構(gòu)發(fā)現(xiàn)源頭數(shù)據(jù)在script腳本中,腳本中基本上就是類似json的數(shù)據(jù)對象,只要拿到j(luò)s腳本的對象數(shù)據(jù)就等于得到了搜索結(jié)果。
python是可以解析js腳本并獲取其中的數(shù)據(jù)對象的,這里推薦chompjs這個庫,chompjs可以直接將js中的數(shù)據(jù)對象返回給python,非常的方便,使用前需要使用pip install chompjs安裝此庫。需要注意的是,chompjs的核心代碼使用c來編寫,因此安裝chompjs需要事先安裝c++構(gòu)建工具。
1. 如果事先沒有安裝c++構(gòu)建工具會報錯
2. 訪問上面錯誤提示中的鏈接下載c++構(gòu)建工具并安裝
安裝成功之后再次執(zhí)行pip install chompjs安裝成功
scrapy shell可以使用交互方式快速驗證提取數(shù)據(jù)的代碼,不需要一次次地運行爬蟲驗證,非常的高效,接下來就演示如何使用scrapy shell把js中的數(shù)據(jù)提取出來。
啟動scrapy shell
scrapy shell "https://wenku.baidu.com/search?word=python&lm=0&od=0&fr=top_home&ie=utf-8&_wkts_=1711155481643&wkQuery=python"
從返回的結(jié)果來看,response已經(jīng)獲取到了爬取的頁面內(nèi)容,接下來主要使用response進行數(shù)據(jù)提取
我們已經(jīng)知道數(shù)據(jù)源在script中,直接使用response.css('script')可以獲取所有的script,我們需要的是第二個script中的腳本
直接取出script中的js腳本,response.css("script::text")[1].get()
最后就是導(dǎo)入chompjs庫,執(zhí)行js代碼并獲取返回的數(shù)據(jù)對象
import chompjs
data = chompjs.parse_js_object(js_code)
data是dict字典對象,需要的數(shù)據(jù)在'sulaData'這個key中,至此,大功告成啦。
簡單做個總結(jié),我認(rèn)為使用scrapy寫爬蟲根本不需要用到像selenium這樣的模擬瀏覽器下載器,解決的辦法就是找到數(shù)據(jù)的源頭,沒有數(shù)據(jù)源動態(tài)網(wǎng)站也無從渲染。不過,這里忽略了一個重要的環(huán)節(jié),就是登錄,不同平臺登錄機制也不同,各種各樣的驗證碼讓人頭疼,正面硬剛很可能碰的頭破血流,因此我不建議把登錄部分寫在爬蟲里,一種更好的方案是單獨開發(fā)一個cookie池服務(wù),cookie池服務(wù)負責(zé)管理所有平臺的用戶登錄,不能自動登錄的就提供手工登錄,并通過web api提供隨機獲取cookie,scrapy通過調(diào)用cookie池的api來獲取cookie實現(xiàn)模擬已登錄用戶狀態(tài)。
eautifulsoup介紹:
第一步:安裝BeautifulSoup4,lxml
pip install BeautifulSoup4
BeautifulSoup 是一個可以從HTML或XML文件中提取數(shù)據(jù)的Python庫
pip install lxml
lxml 是一種使用 Python 編寫的解析庫,可以迅速、靈活地處理 XML 和 HTML
第二步:導(dǎo)包,from bs4 import BeautifulSoup
第三步:實例化對象
html = """
<html>
<head>
<title>The Dormouse's story</title>
</head>
<body>
<p class="story">
Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">
<span>Elsie</span>
</a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
and they lived at the bottom of a well.
</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html, 'lxml') # h:要解析的內(nèi)容 lxml:解析器
知識補充:print(soup.prettify()) # 代碼補全
第四步:打印
一、通過標(biāo)簽選取,會返回包含標(biāo)簽本身及其里面的所有內(nèi)容
# print(soup.head) # 包含head標(biāo)簽在內(nèi)的所有內(nèi)容
# print(soup.p) # 返回匹配的第一個結(jié)果
1.print(soup.head)打印結(jié)果:
<head>
<title>The Dormouse's story</title>
</head>
2.print(soup.p)打印結(jié)果:
<p class="title" name="dromouse"><b><span>The Dormouse's story</span></b></p>
二、打印標(biāo)簽中間的文本內(nèi)容,不包含<>
# .string是屬性,作用是獲取字符串文本
print(soup.html.head.title.string)
print(soup.title.string)
打印結(jié)果都為:The Dormouse's story
三、打印標(biāo)簽名
.name --獲取標(biāo)簽本身名稱
print(soup.title.name)
打印結(jié)果為:title
四、打印屬性的值
.attrs[] --通過屬性拿屬性的值
print(soup.p.attrs['name'])# 獲取p標(biāo)簽name屬性的屬性值
print(soup.a.attrs['id']) # 獲取p標(biāo)簽id屬性的屬性值
print(soup.a['id']) #第二種寫法
print(soup.p['class']) # 以列表得形式保存
print(soup.a['href']) # 也是只返回第一個值
1.print(soup.p.attrs['name'])打印結(jié)果:
dromouse
2.print(soup.p.attrs['id'])和print(soup.a['id'])打印結(jié)果:
link1
3.print(soup.p['class'])打印結(jié)果:
['title', 'asdas']
4.print(soup.a['href'])打印結(jié)果:
http://example.com/elsie
五、打印父標(biāo)簽下的所有子標(biāo)簽
.contents 獲取標(biāo)簽子節(jié)點,以列表形式返回
.children 獲取子節(jié)點,返回的是一個list類型的迭代器
print(soup.body.contents) # a是p的子節(jié)點,獲取P標(biāo)簽所有子節(jié)點內(nèi)容 返回一個list
print(soup.body.children) #返回的是一個list類型的迭代器
1.print(soup.body.contents)的打印結(jié)果:
['\n', <p class="title asdas" name="dromouse"><b><span>The Dormouse's story</span></b></p>, '\n', <p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>, '\n', <p class="story">...</p>, '\n']
2.print(soup.body.children)的打印結(jié)果:
<list_iterator object at 0x000002035ECC7088>
.children 獲取子節(jié)點講解
1.和for循環(huán)一起使用
for i in soup.p.children:
print(i)
打印結(jié)果:
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。