爬蟲實戰:探索XPath爬蟲技巧之熱榜新聞

努力的小雨發表於2024-03-21

之前我們已經詳細討論瞭如何使用BeautifulSoup這個強大的工具來解析HTML頁面,另外還介紹了利用線上工具來抓取HTTP請求以獲取資料的方法。在今天的學習中,我們將繼續探討另一種常見的網路爬蟲技巧:XPath。XPath是一種用於定位和選擇XML文件中特定部分的語言,雖然它最初是為XML設計的,但同樣適用於HTML文件的解析。

HTML和XML有很多相似之處,比如標籤、屬性等,因此XPath同樣可以在HTML文件中有效地定位元素。爬蟲可以利用XPath表示式來指定需要提取的資料的位置,然後透過XPath解析器來解析HTML文件,從而提取所需的資訊。

好的,我們不多說,直接開始今天的任務,爬取36kr的熱榜新聞以及新聞搜尋。

XPath爬蟲

如果對XPath不熟悉也沒關係,可以直接使用它,就能發現它與我們之前使用的BeautifulSoup有著相同的目的。只是在表示式和方法的使用上略有不同。在進行爬蟲之前,我們可以先下載一個XPath工具。之前我們編寫BeautifulSoup程式碼時,需要自行查詢HTML程式碼中的標籤並編寫程式碼進行解析,這樣很費眼。而在瀏覽器中可以使用外掛工具來直接提取XPath元素。

XPath外掛

有很多瀏覽器外掛可供選擇,我們只需直接獲取一個即可。最重要的是,這些外掛可以讓我們在選擇時輕鬆複製表示式,就像這樣:

image

當我開啟外掛工具後,立即觸發左鍵操作,從而開始顯示紅色框框,使用者選擇後,系統會呈現一系列XPath表示式供選擇,使用者只需選取適當的表示式即可。

image

熱榜新聞

會使用工具後,我們將繼續進行資料爬取和頁面資訊解析。在此之前,需要安裝一個新的依賴庫lxml。以下是一個示例程式碼供參考:

from lxml import etree
import requests
 
hot_article_list = []

headers = {
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
def get_hot_article():
    url = 'https://36kr.com/hot-list/catalog'
    response = requests.get(url=url,headers=headers)
    # 獲取的html內容是位元組,將其轉化為字串
    #使用etree進行解析
    data = etree.HTML(response.text)

    # 使用XPath定位元素
    # 提取a標籤的article-item-title文字資料以及url連線
    article_titles = data.xpath("(//a[@class='article-item-title weight-bold'])")
    article_desc = data.xpath("(//a[@class='article-item-description ellipsis-2'])")

    if len(article_titles) == len(article_desc):
        for article, desc in zip(article_titles, article_desc):
            # 獲取元素的連結(href 屬性)
            link = article.get('href')
            # 獲取元素的文字內容
            title = article.text
            desc = desc.text
            hot_article_list.append({
                "title":title,
                "link":link,
                "desc":desc
            })
    else:
        print("未找到指定元素")
print(hot_article_list)

這段程式碼的功能是從36氪網站的熱門文章列表中提取文章的標題、連結和描述資訊,並將這些資訊儲存在一個列表中。其中,lxml庫用於HTML解析,requests庫用於傳送HTTP請求。接著,定義了一個空列表hot_article_list,用於儲存提取的文章資訊。

踩個小坑

在前面已經成功提取了熱門文章標題和連結,接下來通常應該開始逐個訪問這些連結以檢視新聞詳情。然而,在傳送請求獲取單個URL連結時,卻未能獲得預期的新聞資訊,出現了以下情況:

image

通常情況下,網頁中的資料要麼直接包含在靜態HTML中,比如之前我們解析的美食菜譜等;要麼是透過Ajax的HTTP請求獲取的,比如我們嘗試獲取騰訊雲社群的文章列表。通常,這些資料都可以在搜尋中找到相應的匹配項。然而,我花了一個小時的時間仍未能成功獲取所需資訊。最初,我懷疑可能是因為網頁中存在跳轉頁面傳輸資料,因此我特意使用抓包工具進行了下載,但令人失望的是,並沒有發現相關資料。

因此,我又仔細檢查了一遍靜態HTML程式碼,並在程式碼末尾發現了一個奇怪之處——HTML頁面的部分竟然被加密了。讓我們來看看這段程式碼吧。

image

如果你對這些內容感到疑惑,建議再次在搜尋框中輸入相關關鍵字以查詢更多資訊。很可能存在解密函式。果然如此。我們接下來看下。

image

既然官方對資料進行了加密處理,顯然是出於一定的考慮,其中可能包括對爬蟲的防護等因素。鑑於此,我決定不再嘗試對其進行解密操作,這個就這樣吧。

資訊搜尋

36氪網站不僅提供了熱門文章資訊,還支援新聞搜尋功能。讓我們深入探討一下搜尋功能的實現方式。通常情況下,靜態頁面即可滿足需求進行資訊提取。但若希望獲取更多資料,就需要透過傳送ajax請求來實現。

image

接著看程式碼:

from lxml import etree
from urllib.parse import quote
import requests

def get_article_search(keyword):
    qk = quote(keyword)
    url = f'https://36kr.com/search/articles/{qk}'
    response = requests.get(url=url,headers=headers)
    # 獲取的html內容是位元組,將其轉化為字串
    #使用etree進行解析
    data = etree.HTML(response.text)
    # 使用XPath定位元素
    # 提取a標籤的article-item-title文字資料以及url連線
    article_detail = data.xpath("(//p[@class='title-wrapper ellipsis-2']//a)")
    for a_tag in article_detail:
        text = a_tag.xpath("string()").strip()
        url = a_tag.get("href")
        print("文字:", text)
        print("URL連線:", url)



def get_article_url(keyword):

    headers = {
        'Accept': '*/*',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection': 'keep-alive',
        'Content-Type': 'application/json',
        'Cookie': 'Hm_lvt_713123c60a0e86982326bae1a51083e1=1710743069; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2218b40a4b8576e0-0508814adc1724-745d5774-2073600-18b40a4b858109a%22%2C%22%24device_id%22%3A%2218b40a4b8576e0-0508814adc1724-745d5774-2073600-18b40a4b858109a%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; Hm_lvt_1684191ccae0314c6254306a8333d090=1710743069; aliyungf_tc=9f944307bb330cb7a00e123533aad0ee8a0e932e77510b0782e3ea63cddc99cf; Hm_lpvt_713123c60a0e86982326bae1a51083e1=1710750569; Hm_lpvt_1684191ccae0314c6254306a8333d090=1710750569',
        'Origin': 'https://36kr.com',
        'Referer': 'https://36kr.com/',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-site',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0',
        'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    json_data = {
        'partner_id': 'web',
        'timestamp': 1710751467467,
        'param': {
            'searchType': 'article',
            'searchWord': keyword,
            'sort': 'score',
            'pageSize': 20,
            'pageEvent': 1,
            'pageCallback': 'eyJmaXJzdElkIjo5NSwibGFzdElkIjo1MSwiZmlyc3RDcmVhdGVUaW1lIjo3NTU4MSwibGFzdENyZWF0ZVRpbWUiOjIzOTk3LCJsYXN0UGFyYW0iOiJ7XCJwcmVQYWdlXCI6MSxcIm5leHRQYWdlXCI6MixcInBhZ2VOb1wiOjEsXCJwYWdlU2l6ZVwiOjIwLFwidG90YWxQYWdlXCI6MTAsXCJ0b3RhbENvdW50XCI6MjAwfSJ9',
            'siteId': 1,
            'platformId': 2,
        },
    }

    response = requests.post(
        'https://gateway.36kr.com/api/mis/nav/search/resultbytype',
        headers=headers,
        json=json_data,
    )
    
    data = response.json()
    for parsed_data  in data['data']['itemList']:
        widget_title = parsed_data['widgetTitle'].replace('<em>', '').replace('</em>', '')
        print(widget_title)
        widget_url = parsed_data['route']
        print(widget_url)
        
get_article_search('OpenAI')
get_article_url('我要')

get_article_searchget_article_url。這兩個函式都是用來從36氪網站上獲取文章資訊的。

  1. get_article_search(keyword):
    • 首先,將關鍵詞進行URL編碼。
    • 構建搜尋URL併傳送GET請求獲取頁面內容。
    • 使用lxml庫的etree模組解析HTML內容。
    • 使用XPath定位元素,提取文章標題和URL連線。
  2. get_article_url(keyword):
    • 函式中定義了請求頭(headers)和請求體(json_data)。
    • 傳送POST請求到指定的API介面獲取文章URL資料。
    • 解析返回的JSON資料,提取文章標題和URL連線。

總結

在這篇文章中,我們深入學習了XPath作為一種常見的網路爬蟲技巧。XPath是一種用於定位和選擇XML文件中特定部分的語言,儘管最初是為XML設計的,但同樣適用於HTML文件的解析。我們探討了如何使用XPath來定位元素並提取所需資訊。

透過這篇文章的學習,我們對XPath的應用有了更深入的瞭解,也提升了我們在網路爬蟲領域的技能。繼續努力學習和實踐,相信我們可以在爬蟲技術上取得更大的進步!

相關文章