iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
2
自我挑戰組

資料蒐集與分散式運算 30 天系列 第 7

[Day 7] 番外篇 - 工程師的生活就是這麼樸實無華

曾有位前輩分享了一篇 PO 文,內容是一張 Stack Overflow 回答後的截圖外加 Hashtag #工程師假日的浪漫,工程師的浪漫就是即便是假日都還是想逛逛 Stack Overflow 順手解掉幾個問題。在 IT 邦幫忙也有類似的互動,因此我們來運用前幾天分享的爬蟲結合 Line API 每天自動推播與自己設定相關的問題。

架構

首先在技術問答的頁面中,我們可以看到幾個資訊:「問題」、「問題相關 Tag」、「題問時間」、「提問人」、「預覽數」、「回答數」、「是否有最佳解答」、「Like 數」等,今天的目的是為「蒐集近期自己關心的主題且該問題還尚未被解決」,那就應該專注在「問題相關的 Tag」、「是否有最佳解答」與「提問時間」的分析。而從符合上述條件的問題,將會擷取他的問題標題與 URL 透過 Line API 推播給自己。

由於整個流程可以切割為爬蟲與 Line API 串接,因此這次的番外篇會分成兩天,第一天是關於爬蟲程式的修改,第二天是利用 Flask 框架 + Line SDK 做一個簡單的推播媒介。

爬蟲與分析

擷取昨天部分的程式碼,並做適當的編改。首先由於這次要滿足多個條件,因此必須在更上層的 HTML 標籤開始定位(xpath = "//div[@class = 'qa-list']")往下一層層的定位出各個條件。

雖然時間層面只會爬取今天+昨天的問題,但還是要去定位出最後一頁的數字為多少,因為在爬蟲前無法確定這樣的時間區段會佔多少的頁面內容。因此在程式的一開始執行 page_numbers 定位出最後一頁的數字。

再來要符合第一個條件該問題尚未有“最佳解答”,已有最佳解答的問題,在回答欄位會是橘色底的方式呈現,而觀察 HTML 會發現同樣的 <a> 標籤多出了 qa-condition--had-answer 這個 class。我們可以利用這個作為區分。

https://ithelp.ithome.com.tw/upload/images/20200920/20128931yWknvfhE0y.png

第二個條件是符合相關的 Tag,這裡簡單設定目標 Tag 為 python,可以透 find 的方式在這個 HTML 節點(xpath = "//div[@class = 'qa-list']")往下定位。

第三個條件為提問時間,一樣透過 find 的方式往下定位置時間的位置。

from time import sleep
import datetime
import requests
from lxml import etree

def page_numbers(url):
    response = requests.get(url)
    html = etree.HTML(response.text)
    next_tag = html.xpath("//a[@rel='next']")[0] # 定位出下一頁
    final_page = next_tag.getparent().getprevious().find('a').text # 下一頁按鈕的前一個按鈕及為最後一頁的數字
    return int(final_page)
  
def get_title_url(html,target_date):
    element_list = html.xpath("//div[@class = 'qa-list']")
    answered_tag = "qa-condition  qa-condition--has-answer   qa-condition--had-answer  " # 已有最佳解答
    target_date = target_date.strftime("%Y-%m-%d")
    articles = {}
    for element in element_list:
        tags = [t.text for t in element.findall("./div[@class = 'qa-list__content']/div[@class='qa-list__tags']/a")] # 列出所有 Tag
        time = element.find("./div[@class = 'qa-list__content']/div[@class='qa-list__info']/a[1]").text # 定位出時間
        answer_status = element.find("./div[@class = 'qa-list__condition']/a[2]").get('class') # 找出回答標籤的 class 內容
        if answer_status != answered_tag and "python" in tags and time == target_date: # 若三個條件皆符合,則收入為目標問題
            articles[element.find("./div[@class = 'qa-list__content']/h3/a").text] = element.find("./div[@class = 'qa-list__content']/h3/a").get("href")
    return articles
  
  
target_url = "https://ithelp.ithome.com.tw/questions"
today = datetime.date.today()
target_date = today.replace(day=today.day -1)
last_page = page_numbers(target_url)
articles = {}

for page in range(last_page):
    response = requests.get(target_url,params = {'page':page+1})
    html = etree.HTML(response.text)
    articles = {**articles,**get_title_url(html,target_date)}
    last_question_date = html.xpath("//div[@class='qa-list__info']/a[1]")[-1].text
    if datetime.datetime.strptime(last_question_date,"%Y-%m-%d").date() < target_date:
        break
    

完成爬蟲後,明天將透過 Line API 的串接,達成每日接收新問題的小工具。明天見!


上一篇
[Day 6] 靜態爬蟲 - 2
下一篇
[Day 8] 番外篇 - 工程師了生活就是這麼樸實無華 - 2
系列文
資料蒐集與分散式運算 30 天30

1 則留言

0
Bernard
iT邦新手 5 級 ‧ 2020-09-20 15:41:42

推「工程師的生活就是樸實無華」!
週日下午,回去趕稿 /images/emoticon/emoticon02.gif

我要留言

立即登入留言