1. 程式人生 > >【python】爬蟲篇:python對於html頁面的解析(二)

【python】爬蟲篇:python對於html頁面的解析(二)

我,菜雞,有什麼錯誤,還望大家批評指出!!

前言:

根據自己寫的上一篇文章,我繼續更第二部分的內容,詳情請點選如下連結

【python】爬蟲篇:python連線postgresql(一):https://blog.csdn.net/lsr40/article/details/83311860

本文主要介紹了python通過bs4(BeautifulSoupxpath兩種方法來獲取爬到的html頁面上想要的部分!廢話不多說,開始!

正文:

在上一篇文章中我們可以拿到一個叫做rows的物件,這個物件就是資料庫裡一條一條的資料,因此需要遍歷每一條資料,然後開啟url拿到html的頁面資訊再做解析。

1、遍歷rows

遍歷就不用說了吧,for row in rows....

2、通過urllib開啟頁面(當然也有其他的框架,比如requests)

這個說一點:

# 當我如下的方式執行程式碼的時候,有時候程式會假死
# 就是突然卡主,然後什麼都不做
file = urllib.request.urlopen(url)
# 因此要這麼寫,給定一個超時時間,如果請求不到就直接跳過了
file = urllib.request.urlopen(url,timeout=5)

3、對於獲得的頁面html做解析

解析是這樣的,我測試了下我的電腦上執行,差不多1s,可以跑完兩條查到資料庫。然後bs4和xpath的速度沒有什麼特別大的區別,但是xpath確實會快一點。

當我使用bs4的時候遇到RecursionError: maximum recursion depth exceeded in comparison這樣的報錯,原因可以看https://blog.csdn.net/cliviabao/article/details/79927186(cliviabao的原創文章),報錯解釋如下:

file = urllib.request.urlopen(row[2],timeout=5)
    try:
        data = file.read()  # 讀取全部
        soup = BeautifulSoup(data.decode('utf-8'), 'html.parser')
        # 就是在find_all報了錯,因此我想這樣遍歷應該是遞迴遍歷,層數太深
        result = soup.find_all('p', text=True)

# 解決方法有兩種:第一種(治標不治本,叫做把遞迴層數增加)
import sys   
sys.setrecursionlimit(100000) #例如這裡設定為十萬  

# 第二種:避免這種錯誤的查詢,比如你想找一個div,他的class名字叫正文,而且通篇只有這個一個,那麼
# 所以可以去找找find_all可傳入的引數
result = soup.find_all(name='div', attrs={"class": "zhengwen"}, limit=1)

另一種就是xpath,有幾點說明一下:

1、有時候可能會被封ip,雖然爬蟲卻還在爬著資料,不過這些資料肯定都是有問題的,所以要加一個標識,發現爬到的頁面發現ip已經被封了,可以傳送警報,並且插到資料庫的資料就加上某個被封的標識,用來下次重跑這部分資料。

2、有個小插曲,被封了ip之後我怎麼都無法通過xpath的方法來判斷被封了(因為被封的訊息並不是直接寫在html的標籤裡面的,而是在js中write的),所以我只能用python中str的find方法來判斷是否被封(可以新增偏移量,不從頭開始遍歷字串)

find方法的使用:http://www.runoob.com/python/att-string-find.html

3、關於xpath的使用方法:

大家可以百度些文章自己學著寫寫,或者直接開啟網頁,F12進入到開發者模式,找到你要的那部分網頁資訊在哪個標籤中,接下來複製出來他的xpath,如下兩圖所示,複製出來會類似這種東西//*[@id="csdn-toolbar"]就可以直接使用了!

 

程式碼如下:

try: 
    file = urllib.request.urlopen(url,timeout=5)
    data = file.read()  # 讀取全部
    #這裡可以新增上判斷是否被封號的標誌,如果被封號就發提醒,或者在插入資料庫的時候表示下,這資料採集的時候已經被封ip了,所以得重新採集這部分被封ip的資料
    isBan=str(data).find('xxx', 3000)
    if(isBan!=-1):
        string='ip被封'
    else:
        selector = etree.HTML(data)
        data = selector.xpath('//*[@id="js_content"]/p/span/text()')
        for i in data:
            if (i != None):
                string = string + i  # .replace('\'', '''''')

4、關於速度

當然爬蟲的速度是非常需要關注的一個點!首先肯定是越快越好,但是不同網站會做不同的黑名單機制,也就是說你訪問的太快有可能會直接被封ip,因此就需要一臺可以自動換ip的伺服器,當然這就不在討論這段程式碼的範疇之內了。

在這裡,我只考慮的如何加快爬蟲的速度!!

我就想到兩種方法:

1、多執行緒(一段程式碼裡開啟多個執行緒,同時訪問不同頁面)

2、多程序(執行多個python指令碼,來達到同時訪問不同頁面)

我選用第一種方式(完整程式碼):

# 這裡的productList方法只實現了獲取頁面具體的內容,還缺少將獲取到的資料插入資料庫的實現,未完待續!
def productList(rows):
    string=''
    try: 
        file = urllib.request.urlopen(url,timeout=5)
        data = file.read()  # 讀取全部
        isBan=str(data).find('xxx', 3000)
        if(isBan!=-1):
            string='ip被封'
        else:
            selector = etree.HTML(data)
            data = selector.xpath('//*[@id="js_content"]/p/span/text()')
            for i in data:
                if (i != None):
                    string = string + i  # .replace('\'', '''''')
    except Exception as e:
        # e.partial 這個屬性據說是報錯的時候已經接受的html內容,沒有實際測試過
        data = '程式碼異常'
        print('Error', e)

if __name__ == '__main__':
    conn_1 = psycopg2.connect(database="資料庫", user="使用者", password="密碼",
                                   host="ip",
                                   port="埠")
    cur1 = conn_1.cursor()
    # 查出url
    sql1 = "select xxx from xxx"
    cur1.execute(sql1)
    rows = cur1.fetchall()
    print('拉取到資料')
    # 這裡就是開啟10個執行緒來跑productList方法,並且我考慮到了需要批量插入資料,因此一次性往執行緒中傳入10條url,然後將10條url一起插入資料庫(其實可以更多條一起插入)
    with ThreadPoolExecutor(10) as executor:
        for i in range(0, len(rows)//10, 1):
            executor.submit(productList, rows[i*10:(i+1)*10])
    # 結束關閉連線
    conn_1.close()
        

 

以上就是我關於python對應html頁面解析要說的全部內容,大家如果還有什麼問題可以給我留言,或許我能幫上什麼忙~

好了,本人菜雞一個,如果有說的不對的地方,或者不夠合理的處理方式,還望各位大神指點迷津,謝謝各位!!

未完待續!