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
義上講,爬蟲(chóng)只負(fù)責(zé)抓取,也就是下載網(wǎng)頁(yè)。而實(shí)際上,爬蟲(chóng)還要負(fù)責(zé)從下載的網(wǎng)頁(yè)中提取我們想要的數(shù)據(jù),即對(duì)非結(jié)構(gòu)化的數(shù)據(jù)(網(wǎng)頁(yè))進(jìn)行解析提取出結(jié)構(gòu)化的數(shù)據(jù)(有用數(shù)據(jù))。
所以說(shuō),網(wǎng)頁(yè)下載下來(lái)只是第一步,還有重要的一步就是數(shù)據(jù)提取。不同的爬蟲(chóng)想要的數(shù)據(jù)不一樣,提取的數(shù)據(jù)也就不一樣,但提取方法都是類似的。
最簡(jiǎn)單的提取數(shù)據(jù)的方法,就是使用正則表達(dá)式,此種方法簡(jiǎn)單,提取的邏輯也不能復(fù)雜,不然寫(xiě)出的正則表達(dá)式就晦澀難懂,甚至不能提取復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
最終,老猿經(jīng)過(guò)多年的使用經(jīng)驗(yàn),選擇了lxml和xpath來(lái)解析網(wǎng)頁(yè)提取結(jié)構(gòu)化數(shù)據(jù)。順便說(shuō)一下 BeautifulSoup,它也是一個(gè)很棒的解析HTML的工具,可以使用多個(gè)解析器,比如Python標(biāo)準(zhǔn)庫(kù)的parser,但是速度比較慢,也可以使用lxml作為解析器,但是它的使用方法、API跟lxml不太一樣。使用下來(lái),還是lxml的API更舒服。
lxml 對(duì)C語(yǔ)言庫(kù) libxml2和 libxslt進(jìn)行綁定,提供了Pythonic的API,它有一些主要特點(diǎn):
總結(jié)為一句話就是,C語(yǔ)言的速度和Python的簡(jiǎn)易相結(jié)合的神器。
lxml有兩大部分,分別支持XML和HTML的解析:
lxml.etree可以用來(lái)解析RSS feed,它就是一個(gè)XML格式的文檔。然而爬蟲(chóng)抓取的絕大部分都是html網(wǎng)頁(yè),所以,我們這里主要講述lxml.html解析網(wǎng)頁(yè)的方法。
我們下載得到的網(wǎng)頁(yè)就是一串html字符串,如何把它輸入給lxml.html模塊,從而生成html文檔的樹(shù)結(jié)構(gòu)呢?
該模塊提供了幾種不同的方法:
下面我們通過(guò)具體示例來(lái)說(shuō)明上面幾個(gè)方法的不同。
document_fromstring 的使用方法
In [1]: import lxml.html as lh In [2]: z = lh.document_fromstring('<span>abc</span><span>xyz</span>') # 可以看到,它自動(dòng)加了根節(jié)點(diǎn)<html> In [3]: z Out[3]: <Element html at 0x7fc410667b88> In [4]: z.tag Out[4]: 'html' # 還加了<body>節(jié)點(diǎn) In [5]: z.getchildren() Out[5]: [<Element body at 0x7fc4101a3ae8>] # 把字符串的兩個(gè)節(jié)點(diǎn)放在了<body>里面 In [6]: z.getchildren()[0].getchildren() Out[6]: [<Element span at 0x7fc410092bd8>, <Element span at 0x7fc410667c28>]
fragment_fromstring 的使用
In [11]: z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’) --------------------------------------------------------------------------- ParserError Traceback (most recent call last) <ipython-input-11-a11f9a0f71d1> in <module>() ----> 1 z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’) ~/.virtualenvs/py3.6/lib/python3.6/site-packages/lxml/html/__init__.py in fragment_fromstring(html, create_parent, base_url, parser, **kw) 850 raise etree.ParserError( 851 “Multiple elements found (%s)” --> 852 % ‘, ‘.join([_element_name(e) for e in elements])) 853 el = elements[0] 854 if el.tail and el.tail.strip(): ParserError: Multiple elements found (div, div) # 可以看到,輸入是兩個(gè)節(jié)點(diǎn)(element)時(shí)就會(huì)報(bào)錯(cuò) # 如果加上 create_parent 參數(shù),就沒(méi)問(wèn)題了 In [12]: z = lh.fragment_fromstring('<div>abc</div><div>xyz</div>', create_parent='p') In [13]: z.tag Out[13]: 'p' In [14]: z.getchildren() Out[14]: [<Element div at 0x7fc40a41a818>, <Element div at 0x7fc40a41aea8>]
fragments_fromstring 的使用
# 輸入字符串含有一個(gè)節(jié)點(diǎn),則返回包含這一個(gè)節(jié)點(diǎn)的列表 In [17]: lh.fragments_fromstring('<div>abc</div>') Out[17]: [<Element div at 0x7fc40a124ea8>] # 輸入字符串含有多個(gè)節(jié)點(diǎn),則返回包含這多個(gè)節(jié)點(diǎn)的列表 In [18]: lh.fragments_fromstring('<div>abc</div><div>xyz</div>') Out[18]: [<Element div at 0x7fc40a124b88>, <Element div at 0x7fc40a124f98>]
fromstring 的使用
In [27]: z = lh.fromstring('<div>abc</div><div>xyz</div>') In [28]: z Out[28]: <Element div at 0x7fc40a0eb368> In [29]: z.getchildren() Out[29]: [<Element div at 0x7fc410135548>, <Element div at 0x7fc40a0eb2c8>] In [30]: type(z) Out[30]: lxml.html.HtmlElement
這里,fromstring輸入的如果是多個(gè)節(jié)點(diǎn),它會(huì)給加一個(gè)父節(jié)點(diǎn)并返回。但是像html網(wǎng)頁(yè)都是從節(jié)點(diǎn)開(kāi)始的,我們使用fromstring() 和 document_fromstring() 都可以得到完整的網(wǎng)頁(yè)結(jié)構(gòu)。
從上面代碼中我們可以看到,那幾個(gè)函數(shù)返回的都是HtmlElement對(duì)象,也就是說(shuō),我們已經(jīng)學(xué)會(huì)了如何從html字符串得到HtmlElement的對(duì)象,下一節(jié)我們將學(xué)習(xí)如何操作HtmlElement對(duì)象,從中提取我們感興趣的數(shù)據(jù)。
發(fā)送HTTP請(qǐng)求:首先,你需要向目標(biāo)網(wǎng)頁(yè)發(fā)送HTTP請(qǐng)求以獲取其HTML內(nèi)容。這可以通過(guò)Java的內(nèi)置庫(kù)java.net.HttpURLConnection或者使用更高級(jí)的庫(kù)如Apache Http Client OkHttp等來(lái)完成。
·讀取響應(yīng)內(nèi)容:一旦你發(fā)送了請(qǐng)求并收到了響應(yīng),你需要讀取響應(yīng)的內(nèi)容,這通常是HTML格式的字符串。
·解析HTML:然后,你需要解析HTML字符串以提取所需的信息,這可以通過(guò)正則表達(dá)式來(lái)完成。但通常建議使用專門的HTML解析庫(kù),如Jsoup。Jsoup提供了一種非常方便的方式來(lái)解析HTML文檔,并可以通過(guò)類似于CSS或jQuery的選擇器語(yǔ)法來(lái)提取和操作數(shù)據(jù)。
·如果你需要處理更復(fù)雜的網(wǎng)頁(yè)或進(jìn)行更高級(jí)的網(wǎng)頁(yè)抓取和解析任務(wù),你可能還需要考慮使用如Selenium這樣的瀏覽器自動(dòng)化工具來(lái)模擬真實(shí)的瀏覽器行為。但是請(qǐng)注意,頻繁或大規(guī)模地抓取網(wǎng)頁(yè)可能會(huì)違反網(wǎng)站的使用條款甚至可能構(gòu)成法律問(wèn)題。
解析HTML文檔,可以使用一些編程語(yǔ)言中的HTML解析庫(kù)或工具。以下是一些常用的方法:
from bs4 import BeautifulSoup
# 讀取HTML文檔
with open('example.html', 'r') as file:
html = file.read()
# 創(chuàng)建BeautifulSoup對(duì)象
soup = BeautifulSoup(html, 'html.parser')
# 使用BeautifulSoup對(duì)象提取數(shù)據(jù)
# 例如,提取所有的鏈接
links = soup.find_all('a')
for link in links:
print(link.get('href'))
// 讀取HTML文檔
var html = document.documentElement.innerHTML;
// 使用DOM解析器提取數(shù)據(jù)
// 例如,提取所有的鏈接
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
console.log(links[i].getAttribute('href'));
}
無(wú)論你選擇哪種方法,解析HTML文檔的關(guān)鍵是了解HTML的結(jié)構(gòu)和標(biāo)簽,并使用相應(yīng)的解析器或工具來(lái)提取所需的數(shù)據(jù)。
當(dāng)你解析HTML文檔時(shí),你可能會(huì)遇到以下一些常見(jiàn)的任務(wù)和技術(shù):
總的來(lái)說(shuō),解析HTML文檔需要一定的HTML知識(shí)和編程技巧。你需要了解HTML的結(jié)構(gòu)和標(biāo)簽,選擇合適的解析器或工具,使用選擇器來(lái)定位元素,提取所需的數(shù)據(jù),并處理特殊情況。通過(guò)不斷練習(xí)和實(shí)踐,你將能夠更熟練地解析HTML文檔并提取所需的數(shù)據(jù)。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。