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