這篇基本上就是 【Python】鐵人賽草稿自動排程發文神器 & line notifiy 這一篇的撰寫過程
我先說一下,我實在不會python
倒不是說完全沒有碰過,大學因為專題要做機器學習相關所以摸過一下
但是我自己很不喜歡python的縮排機制
再加上畢業後都在做前端相關的,跟JS之類的語言打滾
python的語法幾乎是退化光了,從安裝到各種語法,幾乎都是ChatGPT提供給我的
這個專案最後有放github上,連裡面的readme都有請ChatGPT幫忙 XD
所以我們來GOGO吧
先決定我的程式要有哪些功能
額外功能的話大概是:
首先最基礎需要知道以下幾點:
所以後續會來解析研究iT邦幫忙做出「發文」這個動作時候的request
發送request給主機,接著從收到的html中找出要的資料
這時候就需要取得使用者權限,可能是存在cooike或是session中
發送request時必須攜帶才可以
以後端語言來說,我比較熟悉的是nodeJS或php
但不太確定nodeJS做爬蟲解析到底容不容易,我本身在這之前沒做過爬蟲
而且終極目標我是希望做成exe檔,方便丟到電腦執行
nodeJS的話就是要靠 npm run
所以,雖然python不是我熟悉的語言,我還是選擇用python來實作
而且能夠順便測試是否能夠靠ChatGPT用不熟悉語言開發
接下來要一步一步地從0到變出程式,記錄了踩坑的過程
另外我是已經開發完程式,再回頭寫這篇
重新問一次ChatGPT,然後重新建起
由於ChatGPT的回答具有隨機性,因此回答的命名、細節做法可能和我目前發布的版本不同
如果懶得看這麼繁瑣的步驟,也可以直接跳最後看結論
沒錯,我連python要怎麼裝都忘記了,所以我有問ChatGPT要怎麼裝
你好!要安裝Python,你可以按照以下步驟進行:
前往官方網站:
開啟瀏覽器,前往 Python 的官方網站。網址是:https://www.python.org/。
下載適用於你的作業系統的安裝程式:
在官網首頁,你會看到一個大大的「下載」按鈕。點擊它,系統會自動選擇適用於你作業系統的版本,然後你就可以開始下載。
執行安裝程式:
下載完成後,找到安裝檔案並執行它。安裝程式會引導你完成安裝過程。
選擇安裝選項:
在安裝過程中,會有一個選項是「Add Python X.Y to PATH」(X.Y 代表你安裝的 Python 版本)。請確保這個選項被勾選上,這樣在命令列中才能直接執行 Python。
完成安裝:
完成上述步驟後,等待安裝程式執行完畢。一旦完成,你就成功安裝了 Python。
希望這個步驟對你有所幫助!如果你有任何問題或需要進一步的協助,隨時告訴我。
OK 這一段很單純,就是去官網載,然後裝
由於提到「發文」這個動作其實就是要發出request
所以來問ChatGPT要怎麼用python來發request
要使用 Python 發出 HTTP 請求,你可以使用
requests
模組。以下是一個簡單的範例,示範如何使用 Python 來發出 GET 請求:首先,確保你已經安裝了
requests
模組。如果你還沒有安裝,可以執行以下指令:pip install requests
接著,你可以在 Python 程式中使用以下程式碼來發出 GET 請求:
import requests url = '<https://api.example.com/endpoint>' # 將這裡的 URL 替換成你想要訪問的網址 response = requests.get(url) if response.status_code == 200: print('請求成功!') print('回應內容:', response.text) else: print(f'請求失敗,錯誤碼:{response.status_code}')
這段程式碼會向指定的 URL 發出一個 GET 請求,然後檢查回應的狀態碼。如果狀態碼是 200,表示請求成功,然後你可以透過
response.text
來取得回應的內容。如果你需要發出其他類型的請求(例如 POST、PUT、DELETE 等),可以使用
requests.post()
、requests.put()
、requests.delete()
等函數。希望這能幫到你!如果你有任何進一步的問題,歡迎隨時詢問。
好耶,他跟我說要用 requests
這個套件,然後也教我要怎麼安裝
不過我很快就發現一個問題:我使用pip的時候,系統回我沒有這個指令
若你是 Python 2.7.9 以及之後的版本,或是 Python 3.4 以及之後的版本,
pip
已經預先安裝了。如果你的 Python 版本符合上述條件,但你確定
pip
還未安裝,可以在命令列中輸入:pip --version
這會顯示
pip
的版本信息。然而,如果你使用的是較早版本的 Python(例如 Python 2.7.8 以及之前的版本,或是 Python 3.3 以及之前的版本),你可能需要手動安裝
pip
。以下是手動安裝pip
的步驟:
下載
get-pip.py
:打開瀏覽器,前往 get-pip.py 並將網頁上的程式碼儲存為
get-pip.py
。執行安裝:
在命令列中,使用以下指令執行
get-pip.py
:python get-pip.py
如果你同時安裝了 Python 2 和 Python 3,你可能需要使用
python3
來執行get-pip.py
:python3 get-pip.py
檢查安裝:
安裝完成後,你可以使用以下指令來確認
pip
是否成功安裝:pip --version
希望這能幫到你!如果你遇到了任何問題,隨時向我詢問。
OK,基本上到這邊基礎安裝就完成了,後續有其他套件要安裝就都不是大問題了
最基本的requests
套件裝好了
接下來先來研究到底要發什麼reques才能取得列表
yes… 我完全忘記python寫好的東西要怎麼跑起來
運行一個寫好的 Python 程式可以通過命令列或集成開發環境(IDE)來進行。以下是兩種方式:
方式一:使用命令列
開啟命令列:
- 在 Windows 上,可以使用開始菜單中的「命令提示字元」或「PowerShell」。
- 在 macOS 或 Linux 上,可以使用終端機。
導航至程式所在的目錄:
使用
cd
命令(change directory)來進入存放你的 Python 程式的目錄。例如,如果你的程式位於
C:\\Users\\YourUsername\\Documents
(Windows)或是/Users/YourUsername/Documents
(macOS/Linux)下,你可以使用:cd Documents
執行程式:
一旦你進入了程式所在的目錄,你可以使用
python
命令來執行 Python 程式。例如:python your_program.py
這裡的
your_program.py
是你想要執行的 Python 檔案的名稱。方式二:使用集成開發環境(IDE)
如果你使用一個 IDE(例如 Visual Studio Code、PyCharm、Jupyter Notebook 等),通常你可以直接在 IDE 中打開並運行 Python 程式。
打開 IDE:
開啟你選擇的 IDE。
打開程式:
在 IDE 中打開你想要執行的 Python 程式。
選擇運行:
大多數 IDE 都會提供一個「運行」或「執行」按鈕,你可以通過點擊這個按鈕來運行你的程式。
無論你選擇哪種方式,都需要確保你已經在你的系統中安裝了 Python,並且已經將
python
或python3
加入了環境變數中,這樣你才能在命令列中使用python
命令來執行程式。希望這能幫到你!如果你遇到了任何問題,請隨時告訴我。
看起來在你的系統中,無法找到
python
命令。這可能是由於環境變數未正確設定所致。你可以嘗試以下方法:
使用
python3
命令:在一些系統中,Python 3 的執行命令可能是
python3
而不是python
。你可以試著在命令列中輸入:python3 your_program.py
如果這樣可以正常運行,這表示在你的系統中
python3
是正確的 Python 執行命令。確認 Python 是否正確安裝:
你可以再次確認一下是否成功安裝了 Python。請在終端機中輸入:
python --version
或者如果是 Python 3:
python3 --version
如果顯示了 Python 的版本號,這表示 Python 已經正確安裝了。
檢查環境變數:
確保你已經將 Python 的安裝路徑加入了系統的環境變數中。這樣系統才能找到
python
命令。
對於 macOS 或 Linux,你可以在終端機中輸入以下命令檢查:
echo $PATH
確保 Python 的安裝路徑(通常是
/usr/local/bin
或類似的路徑)包含在其中。對於 Windows,請確保 Python 安裝的路徑已添加到系統的 PATH 環境變數中。
如果你仍然遇到問題,請提供更多詳細的系統和安裝信息,我會盡力幫助你解決問題。
哦~要用python3 不是python
OK確定執行python最基本的知識有了,那麼來研究有關request的結構
首先來到自己的文章列表,可以看到這邊有一些草稿
然後觀察取得這個頁面的request
以我自己的頁面為例,發文列表的網址是
https://ithelp.ithome.com.tw/users/20135750/articles
請求方式是 GET,可以得到一串html的內容
其中有一個關鍵是「20135750」這個部分,可以猜測這個是使用者的id
這裡先不爬蟲獲取,先固定url就好
import requests
url = 'https://ithelp.ithome.com.tw/users/20135750/articles' # 將這裡的 URL 替換成你想要訪問的網址
response = requests.get(url)
if response.status_code == 200:
print('請求成功!')
print('回應內容:', response.text)
else:
print(f'請求失敗,錯誤碼:{response.status_code}')
執行之後馬上得到,錯誤碼:403
錯誤碼 403 代表你向該網站發出的請求被伺服器拒絕了,通常是由於權限問題或者伺服器的安全性設定所致。
在這種情況下,你可以考慮以下幾點:
確認網址的正確性:
確保
url
變數中的網址正確,並且可以在瀏覽器中正常訪問。網站需要身份驗證:
有些網站需要身份驗證才能訪問特定頁面。如果是這種情況,你可能需要提供合適的帳號和密碼,或者使用 API 密鑰來訪問該頁面。
模擬瀏覽器行為:
一些網站可能會檢測到你的請求是來自於爬蟲程序而非瀏覽器,並因此拒絕訪問。你可以嘗試模擬瀏覽器的行為,例如設置 User-Agent 標頭,以使你的請求看起來更像是來自於瀏覽器。
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'} response = requests.get(url, headers=headers)
如果你仍然遇到問題,請檢查網站的使用條款以確保你的使用方式合法,並確保你的請求不會違反網站的規則。如果這是一個需要身份驗證或授權的網站,請參考相關的 API 文件或聯繫網站管理員以獲取更多幫助。
選了第三個解法,加上這個headers之後,馬上可以獲取一大堆html
好,我還不會python的function寫法
很好,你成功解決了問題!
要將你的程式轉換成一個函數,你可以按照以下步驟進行:
import requests def get_url_content(url): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'} response = requests.get(url, headers=headers) if response.status_code == 200: print('請求成功!') return response.text else: print(f'請求失敗,錯誤碼:{response.status_code}') return None url = 'https://ithelp.ithome.com.tw/users/20135750/articles' content = get_url_content(url) if content is not None: print('回應內容:', content)
在這個範例中,我們將原本的程式轉換成了一個名為
get_url_content
的函數。這個函數接受一個 URL 作為參數,並返回請求的內容(如果請求成功的話)。你可以將其他的 URL 傳遞給這個函數,以獲得不同網頁的內容。
希望這對你有所幫助!如果你有任何進一步的問題,請隨時告訴我。
抽成function好多了,但是檢視了一下,得到的html沒有包含草稿的文章部分
草稿只有登入狀態下,又是本人的頁面才會顯示
所以我們從中去找尋cookie,在要求標頭的地方,找到了cookie (反白的的部分)
好的,顯然我們的請求也要帶這些東西
繼續來問ChatGPT
要在請求的 header 中包含 cookie,你可以在
headers
字典中加入Cookie
標頭。以下是一個範例:import requests def get_url_content_with_cookie(url, cookie_string): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'Cookie': cookie_string # 在這裡加入 cookie 字串 } response = requests.get(url, headers=headers) if response.status_code == 200: print('請求成功!') return response.text else: print(f'請求失敗,錯誤碼:{response.status_code}') return None url = '<https://ithelp.ithome.com.tw/users/20135750/articles>' cookie = 'your_cookie_string_here' # 將你的 cookie 字串替換在這裡 content = get_url_content_with_cookie(url, cookie) if content is not None: print('回應內容:', content)
在這個範例中,我們將
get_url_content
函數擴展為get_url_content_with_cookie
函數,並新增了一個名為cookie_string
的參數。這個參數用於傳遞 cookie 字串。當你呼叫這個函數時,將你的 cookie 字串傳遞給
cookie_string
參數,它就會被包含在請求的 header 中。請記得將
your_cookie_string_here
替換為你實際的 cookie 字串。希望這能對你有所幫助!如果你有任何進一步的問題,請隨時告訴我。
於是把上述的內容依此類推成我們的程式碼
把cookie帶入之後真的成功了,得到了一大堆的html
用F12觀察列表中草稿的html
找到我們想要的html div程式碼
每個文章的區塊為 class_='qa-list profile-list'
的div
所以問ChatGPT要怎麼找到、定位這些區塊
你可以使用 Python 中的解析庫(如Beautiful Soup)來解析 HTML,然後找出符合特定條件的元素。
首先,你需要安裝 Beautiful Soup:
pip install beautifulsoup4
接下來,你可以使用以下程式碼來找出所有符合特定 class 的
<div>
元素:from bs4 import BeautifulSoup # 假設 content 是你從網頁獲得的 HTML 內容 # content = get_url_content_with_cookie(url, cookie) # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找出所有 class 為 "qa-list profile-list" 的 <div> 元素 divs = soup.find_all('div', class_='qa-list profile-list') # 印出所有找到的元素 for div in divs: print(div.prettify()) # prettify() 會美化輸出
這個程式碼會使用 Beautiful Soup 解析 HTML,然後找出所有 class 為 "qa-list profile-list" 的
<div>
元素。如果你想要進一步處理這些元素,你可以在
for
迴圈中對每個div
進行操作,或者提取出內部的文字或其他元素。請確保
content
變數包含了你從網頁獲得的 HTML 內容。希望這可以幫到你!如果你有任何進一步的問題,請隨時告訴我。
經過以上的處理
我們可以拿到divs 這個陣列,陣列裡面每個都大概像是下面這樣
<div class="qa-list profile-list">
<div class="profile-list__condition">
<a class="qa-condition ">
<span class="qa-condition__count">0</span>
<span class="qa-condition__text">Like</span></a>
<a class="qa-condition ">
<span class="qa-condition__count">0</span>
<span class="qa-condition__text">留言</span>
</a>
<a class="qa-condition ">
<span class="qa-condition__count">0</span>
<span class="qa-condition__text">瀏覽</span>
</a>
</div>
<div class="profile-list__content">
<h3 class="qa-list__title">
<span class="title-badge title-badge--draft">
草稿
</span>
<span class="title-badge title-badge--tech">
技術
</span>
<a href="
https://ithelp.ithome.com.tw/articles/10318302/draft
" class="qa-list__title-link">
【Day5】指南:讓ChatGPT成為程式碼的開發/運維好夥伴
</a>
</h3>
<div class="qa-list__tags">
<a href="/tags/articles/15th鐵人賽" class="tag">15th鐵人賽</a>
<a href="/tags/articles/chatgpt" class="tag">chatgpt</a>
<a href="/tags/articles/程式碼開發" class="tag">程式碼開發</a>
<a href="/tags/articles/程式碼運維" class="tag">程式碼運維</a>
<a href="/tags/articles/javascript" class="tag">javascript</a>
</div>
<div class="qa-list__info">
<a title="2023-09-14 23:33:13" class="qa-list__info-time">2023-09-14</a> 由
<a href="https://ithelp.ithome.com.tw/users/20135750/profile" class="qa-list__info-link">一宵三筵
</a>
分享
</div>
</div>
</div>
發現草稿的class name 是 title-badge--draft
所以我們希望陣列中可以留下有包含<span class="title-badge title-badge--draft">
的就好
要篩選出
divs
中內部包含<span class="title-badge title-badge--draft">
的<div>
元素,你可以使用 Python 的列表推導式(list comprehension)來進行篩選。以下是一個範例:from bs4 import BeautifulSoup # 假設 content 是你從網頁獲得的 HTML 內容 # content = get_url_content_with_cookie(url, cookie) # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找出所有 class 為 "qa-list profile-list" 的 <div> 元素 divs = soup.find_all('div', class_='qa-list profile-list') # 利用列表推導式篩選出包含指定 <span> 的 <div> 元素 filtered_divs = [div for div in divs if div.find('span', class_='title-badge title-badge--draft') is not None] # 印出符合條件的 <div> 元素 for div in filtered_divs: print(div.prettify()) # prettify() 會美化輸出
在這個程式碼中,我們使用列表推導式來遍歷
divs
列表,並對每個<div>
元素檢查是否內部包含指定的<span>
元素。如果包含,則將這個<div>
元素添加到filtered_divs
列表中。最終,
filtered_divs
中會包含所有符合條件的<div>
元素。希望這可以幫助你達到你的目標!如果有任何其他問題,請隨時提出。
如此一來,我們的陣列裡面就會只有草稿了
接下來,要從div中,只取出以下這個標題的部分
<a href="https://ithelp.ithome.com.tw/articles/10318302/draft"
class="qa-list__title-link">
【Day5】指南:讓ChatGPT成為程式碼的開發/運維好夥伴
</a>
看起來每篇文章的草稿連結都是 https://ithelp.ithome.com.tw/articles/10318302/draft
這樣的格式,然後文章會有一個ID 10318302
因此,不但要從div中找出該,還要個別分出:
https://ithelp.ithome.com.tw/articles/10318302/draft
【Day5】指南:讓ChatGPT成為程式碼的開發/運維好夥伴
然後再從href 中找出 id
10318302
首先,從一大串html中,找 且 class="qa-list__title-link" 的語法我們已經會了
從取div的語法類推可得為
title_link = div.find('a', class_='qa-list__title-link')
接著我們想要知道要怎麼取出元素的內文和href
你可以使用 Beautiful Soup 的
.find()
方法來找到這個<a>
元素,然後分別獲取href
屬性和內部的文字。以下是範例程式碼:
from bs4 import BeautifulSoup # 假設你已經取得了包含 <a> 的 div 元素 # 例如:div = soup.find('div', class_='qa-list__title') # 找到 <a> 元素 a_element = div.find('a', class_='qa-list__title-link') # 取得 href 屬性 href = a_element['href'] # 取得內部文字 text = a_element.text.strip() # 使用 strip() 移除空白 print(f'href: {href}') print(f'Text: {text}')
在這個程式碼中,我們首先使用
.find()
方法找到包含<a>
元素的div
。接著,我們再次使用.find()
方法找到這個<a>
元素。然後,我們可以通過索引['href']
取得href
屬性,並使用a_element.text
取得內部文字。希望這對你有所幫助!如果你有任何其他問題,請隨時提出。
ok! 前連面的find他也幫我寫了,驗證了我知道的方式是對的,也了解
href = a_element['href']
text = a_element.text.strip()
這樣的寫法就能從元素中取得他的attr與內容
最後,為了方便我們要另外取出id
你可以使用 Python 的正規表達式模組
re
來匹配並提取 URL 中的指定部分。以下是一個示例程式碼,用來從給定的 URL 中提取articles/
後面的數字部分:import re url = "<https://ithelp.ithome.com.tw/articles/10318302/draft>" # 使用正規表達式匹配 URL 中的數字部分 match = re.search(r'/articles/(\d+)', url) if match: article_id = match.group(1) print(f'文章 ID: {article_id}') else: print('未找到文章 ID')
這個程式碼使用了正規表達式
r'/articles/(\\d+)'
,其中:
/articles/
匹配 URL 中的/articles/
字符串。(\\d+)
匹配並捕獲一個或多個數字。
re.search()
函數將在 URL 中搜索並匹配這個正規表達式,如果匹配成功,就可以使用match.group(1)
來取得捕獲的數字部分,即文章的 ID。希望這對你有所幫助!如果你有任何其他問題,請隨時提出。
綜合以上,我們可以實作一個找草稿的文章列表,並把資料整理成Array<{link: string, text: string, id: string}>
的樣子
用嘴寫程式,開始!
你可以創建一個名為
extract_articles_info
的函數,該函數接受url
和cookie
作為參數,然後執行你所需的操作。以下是完整的程式碼:import requests from bs4 import BeautifulSoup import re def extract_articles_info(url, cookie): # 使用 get_url_content_with_cookie 函數獲取 HTML 內容 content = get_url_content_with_cookie(url, cookie) # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找出所有 class 為 "qa-list profile-list" 的 <div> 元素 divs = soup.find_all('div', class_='qa-list profile-list') # 篩選出包含 <span class="title-badge title-badge--draft"> 的 <div> 元素 filtered_divs = [div for div in divs if div.find('span', class_='title-badge title-badge--draft') is not None] # 初始化結果列表 result = [] # 逐一處理符合條件的 <div> 元素 for div in filtered_divs: # 找到 <a> 元素 a_element = div.find('a', class_='qa-list__title-link') if a_element: # 取得 href 屬性 link = a_element['href'].strip() # 取得內部文字 text = a_element.text.strip() # 使用正規表達式從 link 中提取文章 ID match = re.search(r'/articles/(\d+)', link) if match: article_id = match.group(1) else: article_id = '' # 將結果添加到列表中 result.append({'link': link, 'text': text, 'id': article_id}) return result # 測試函數 url = 'https://ithelp.ithome.com.tw/users/20135750/articles' cookie = 'your_cookie_string_here' # 將你的 cookie 字串替換在這裡 articles_info = extract_articles_info(url, cookie) for article in articles_info: print(article)
這個
extract_articles_info
函數會根據你的需求,解析 HTML、篩選元素、提取所需的資訊,然後返回一個包含文章連結、文字和 ID 的字典陣列。只需將url
和cookie
傳遞給這個函數,它就會返回結果。請記得將
your_cookie_string_here
替換為你實際的 cookie 字串。希望這有幫助!如果有任何問題,請隨時提出。
雖然他說這是完整的程式碼,不過他並沒有幫我把get_url_content_with_cookie
列在裡面
不過沒關係,總之我們整理一下,並且把userID先單獨抽成一個變數,再把他塞到url裡面
為此,我還特地問了
在 Python 中,你可以使用 f 字符串(格式化字符串)來達到類似於 JavaScript 中的模板字串的效果。
在你的例子中,你可以這樣寫:
userId = '20135750' url = f'<https://ithelp.ithome.com.tw/users/{userId}/articles>'
這樣,
url
變數將包含你所期望的 URL。這種方法稱為 f 字符串,它允許你在字符串中插入變量的值。在字符串前加上
f
,然後在字符串中用{}
括起來的地方插入變量名。希望這能幫到你!如果你有任何進一步的問題,請隨時告訴我。
OK!OK! 於是自己整理一下,就變成這樣
import requests
from bs4 import BeautifulSoup
import re
def get_url_content_with_cookie(url, cookie_string):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Cookie': cookie_string # 在這裡加入 cookie 字串
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print('請求成功!')
return response.text
else:
print(f'請求失敗,錯誤碼:{response.status_code}')
return None
def extract_articles_info(userId, cookie):
url = f'https://ithelp.ithome.com.tw/users/{userId}/articles'
# 使用 get_url_content_with_cookie 函數獲取 HTML 內容
content = get_url_content_with_cookie(url, cookie)
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(content, 'html.parser')
# 找出所有 class 為 "qa-list profile-list" 的 <div> 元素
divs = soup.find_all('div', class_='qa-list profile-list')
# 篩選出包含 <span class="title-badge title-badge--draft"> 的 <div> 元素
filtered_divs = [div for div in divs if div.find('span', class_='title-badge title-badge--draft') is not None]
# 初始化結果列表
result = []
# 逐一處理符合條件的 <div> 元素
for div in filtered_divs:
# 找到 <a> 元素
a_element = div.find('a', class_='qa-list__title-link')
if a_element:
# 取得 href 屬性
link = a_element['href'].strip()
# 取得內部文字
text = a_element.text.strip()
# 使用正規表達式從 link 中提取文章 ID
match = re.search(r'/articles/(\d+)', link)
if match:
article_id = match.group(1)
else:
article_id = ''
# 將結果添加到列表中
result.append({'link': link, 'text': text, 'id': article_id})
return result
# 測試函數
userId = '20135750'
cookie = 'cookie' # 將你的 cookie 字串替換在這裡
articles_info = extract_articles_info(url, cookie)
for article in articles_info:
print(article)
我們的articles_info
陣列就會變成這樣
[
{'link': 'https://ithelp.ithome.com.tw/articles/10318302/draft',
'text': '【Day5】指南:讓ChatGPT成為程式碼的開發/運維好夥伴',
'id': '10318302'
},
{'link': 'https://ithelp.ithome.com.tw/articles/10317444/draft',
'text': '【Day4】量身打造:利用Custom instructions (自訂指令) 來打造工程師的專屬助手',
'id': '10317444'
},
{'link': 'https://ithelp.ithome.com.tw/articles/10316704/draft',
'text': '【Day3】示範:使用chatGPT撰寫程式解決算術相關問題',
'id': '10316704'
}
]
先來建立一篇測試用的草稿,並按下儲存草稿,觀察裡面的From Data
經過觀察與測試
儲存草稿的url格式是
https://ithelp.ithome.com.tw/articles/{id}/draft
發表文章的url格式是
https://ithelp.ithome.com.tw/articles/{id}/publish
儲存草稿與發表正式文章的payload都長一樣,都是
{
_token: string,
group: 'tech' #技術文章固定為tech
_method: 'PUT'
subject: string,
descripption: string,
tags[]: string #第一個tag
tags[]: string # 第二個tag, 依此類推
}
這邊有幾個點要注意:
tags[]
_token=jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD&group=tech&_method=PUT&subject=test%E6%A8%99%E9%A1%8C&description=test%E5%85%A7%E6%96%87&tags%5B%5D=tag1&tags%5B%5D=test2&tags%5B%5D=%E6%A8%99%E7%B1%A43
可以看到是 「tags%5B%5D=tag1
&tags%5B%5D=test2
」
文字經過decode解碼,然後多個tag以字串相接黏在一起
綜合上述三點,我們要發文之前,除了指定該文章的id之外,還要取得草稿頁面中的一些資訊
所以我們逐一的來解決這些問題
這個token一定是藏在程式裡的某個地方,或是取得頁面的時候會一起傳輸過來的
查看了單純的網路傳輸,好像沒有一隻api會特地傳這個token
於是我開了F12,在草稿頁面的html中找尋這組token
讓我找到三個地方有寫這個token
會藏在一個隱藏的input中
<input name="_token" type="hidden" value="jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD">
還有一組script中,要發出request前,會在header中加入
{'X-CSRF-TOKEN': 'jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD'}
所以我們就是先獲取草稿頁面html,然後從草稿頁面中拉出<input name="_token">
的value
突然發現上面都是針對calss,這次要針對name就不會寫了,只好:
你可以使用 BeautifulSoup 來解析 HTML,然後找到這個
<input>
元素,並取得它的value
屬性。以下是一個範例程式碼:
from bs4 import BeautifulSoup # 假設 content 是你從網頁獲得的 HTML 內容 # content = get_url_content_with_cookie(url, cookie) # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找到 <input> 元素 input_element = soup.find('input', {'name': '_token', 'type': 'hidden'}) # 取得 value 屬性 if input_element: token_value = input_element['value'] print(f'_token 的 value 為: {token_value}') else: print('未找到 _token 元素')
這個程式碼首先使用 BeautifulSoup 解析 HTML,然後使用
.find()
方法找到name
屬性為_token
、type
屬性為hidden
的<input>
元素。接著,我們取得這個元素的value
屬性。如果找到了符合條件的
<input>
元素,則會取得它的value
屬性並印出。如果找不到符合條件的元素,則會顯示一條提示訊息。希望這對你有所幫助!如果你有任何其他問題,請隨時提出。
所以我們就先來定義function
get_draft_content
目標是找出指定的文章id裏,所有準備可以拿來正式發文的資料
以下先找到token
def get_draft_content(id, cookie):
url = f'https://ithelp.ithome.com.tw/articles/{id}/draft'
content = get_url_content_with_cookie(url, cookie)
soup = BeautifulSoup(content, 'html.parser')
input_element = soup.find('input', {'name': '_token', 'type': 'hidden'})
if input_element:
token = input_element['value']
傳送要把標題、內文、標籤都再帶給api一次
所以一樣就是從html中去找出標題、內文、標籤的tag
<input name="subject" >
的input中的valuesubject = soup.find('input', {'name': 'subject'})['value']
<textarea name="description">
的內容,轉回markdown文字再儲存傳輸基本上直接獲取的頁面中,原本的text內容就是markdown文字了
所以只要
description = soup.find('textarea', {'name': 'description'}).text
但我這邊繼續實驗用嘴寫程式
你可以使用 Beautiful Soup 解析 HTML,然後找到
<select>
元素,遍歷所有<option>
元素,並提取其value
屬性的值。以下是一個示例程式碼:
from bs4 import BeautifulSoup # 假設 content 是你的 HTML 字符串 # content = '<select id="tags" name="tags[]" class="form-control select2-hidden-accessible" multiple="" tabindex="-1" aria-hidden="true"> ...' # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找到名為 "tags" 的 <select> 元素 select_element = soup.find('select', id='tags') # 初始化一個空的列表 tags = [] # 遍歷所有 <option> 元素 for option in select_element.find_all('option'): value = option['value'] tags.append(value) print(tags)
這個程式碼會解析 HTML,找到名為 "tags" 的
<select>
元素,然後遍歷所有的<option>
元素,提取它們的value
屬性的值並添加到tags
列表中。最終,
tags
列表會包含所有的選項值。
希望這可以幫到你!如果你有任何進一步的問題,請隨時告訴我。
有關寫法我還是不太滿意,我不喜歡遍歷之後append進去
當然可以!你可以使用列表推導式來簡化這段程式碼。這樣可以更簡潔地遍歷
<option>
元素並提取它們的value
屬性。以下是優化後的程式碼:
from bs4 import BeautifulSoup # 假設 content 是你的 HTML 字符串 # content = '<select id="tags" name="tags[]" class="form-control select2-hidden-accessible" multiple="" tabindex="-1" aria-hidden="true"> ...' # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找到名為 "tags" 的 <select> 元素 select_element = soup.find('select', id='tags') # 使用列表推導式來提取所有的 option 的 value 屬性值 tags = [option['value'] for option in select_element.find_all('option')] print(tags)
這個優化後的程式碼具有相同的功能,但是使用了列表推導式來簡化遍歷過程。這樣可以使程式碼更簡潔和易讀。
希望這能符合你的需求!如果你有任何其他問題,請隨時告訴我。
OK! 綜合以上,我們可以完成我們的get_draft_content
def get_draft_content(id, cookie):
url = f'https://ithelp.ithome.com.tw/articles/{id}/draft'
content = get_url_content_with_cookie(url, cookie)
soup = BeautifulSoup(content, 'html.parser')
input_element = soup.find('input', {'name': '_token', 'type': 'hidden'})
if input_element:
token = input_element['value']
# 使用 BeautifulSoup 解析 HTML
subject = soup.find('input', {'name': 'subject'})['value']
description = soup.find('textarea', {'name': 'description'}).text
# 找到名為 "tags" 的 <select> 元素
select_element = soup.find('select', id='tags')
# 使用列表推導式來提取所有的 option 的 value 屬性值
tags = [option['value'] for option in select_element.find_all('option')]
return {
'token': token,
'subject': subject,
'description': description,
'tags': tags,
}
前面我們已經可以拿到發文所需的所有內容了:token
、subject
、description
、tags
接下來就是創造發送的post function
帶著期待又怕受傷害的心情問看看ChatGPT可不可以幫我實作出來
你可以實作
publish
函數如下:import requests def publish(id, cookie): # 使用 get_draft_content 函數取得草稿資訊 draft_info = get_draft_content(id, cookie) # 檢查是否成功取得草稿資訊 if not draft_info: print(f'無法取得草稿資訊,請檢查網路連線或登入狀態') return # 解構草稿資訊 token = draft_info['token'] subject = draft_info['subject'] description = draft_info['description'] tags = draft_info['tags'] # 構建 URL url = f'https://ithelp.ithome.com.tw/articles/{id}/publish' # 構建 headers headers = { 'Cookie': cookie, 'X-CSRF-TOKEN': token, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' } # 構建傳送資料 data = { '_token': token, 'group': 'tech', '_method': 'PUT', 'subject': subject, 'description': description } # 將 tags[] 加入傳送資料 data.update({'tags[]': tag for tag in tags}) # 發送 POST request response = requests.post(url, headers=headers, data=data) if response.status_code == 200: print('發布成功!') else: print(f'發布失敗,錯誤碼:{response.status_code}')
這個
publish
函數使用了你之前所提供的get_draft_content
函數,並根據得到的草稿資訊構建了相應的 URL、headers 和傳送資料。最後,它使用requests.post
方法發送了一個 POST request。希望這滿足了你的需求!如果有任何進一步的問題,請隨時提出。
幾乎、幾乎要對了
還記得我說的tags的麻煩問題嗎
ChatGPT 提供的
data.update({'tags[]': tag for tag in tags})
會造成發出去的文只有最後一個標籤,也就是陣列中若有三個標籤,只會留下最後一個
再觀察一下原本網站儲存的時候的資料
也就是原本的物件如果是這樣
{
_token: 'jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD'
group: 'tech'
_method: 'PUT'
subject: 'test標題'
description: 'test內文'
tags[]: ['tag1', 'test2','標籤3'
}
就要變成這樣
_token=jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD&group=tech&_method=PUT&subject=test%E6%A8%99%E9%A1%8C&description=test%E5%85%A7%E6%96%87&tags%5B%5D=tag1&tags%5B%5D=test2&tags%5B%5D=%E6%A8%99%E7%B1%A43
你可以使用
urllib.parse.urlencode()
函數將你的資料字典轉換成 URL-encoded 格式。以下是一個示例程式碼:from urllib.parse import urlencode data = { '_token': 'jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD', 'group': 'tech', '_method': 'PUT', 'subject': 'test標題', 'description': 'test內文', 'tags[]': ['tag1', 'test2', '標籤3'] } encoded_data = urlencode(data, doseq=True) print(encoded_data)
這個程式碼會將你的資料字典轉換成 URL-encoded 格式,並且使用
doseq=True
參數來處理陣列。輸出結果將會是:
_token=jtjNFkKoMI3CBynzIXpCO1vTN3UxyqAnhIQGQQXD&group=tech&_method=PUT&subject=test%E6%A8%99%E9%A1%8C&description=test%E5%85%A7%E6%96%87&tags%5B%5D=tag1&tags%5B%5D=test2&tags%5B%5D=%E6%A8%99%E7%B1%A43
這樣的字串就可以直接用於 POST request 中的 body。
希望這對你有所幫助!如果你有任何其他問題,請隨時告訴我。
哦哦經過這樣處理之後,發出的資料正確了
不過還是有遇到 request 405的問題
如果你在發布時收到 HTTP 錯誤碼 405,這表示該 URL 不接受 POST 請求,可能僅接受 GET 或其他方法。
有幾個可能的原因:
- 錯誤的 HTTP 方法: 確保該 URL 預期的 HTTP 方法是 POST。你在
data
中也使用了_method: 'PUT'
,如果該 URL 預期的方法不是 PUT,也可能導致錯誤。- 伺服器端的限制: 某些網站可能會限制特定 URL 或端點的訪問方式。這可能需要查看該網站的 API 文件或聯繫相應的支援團隊以獲取正確的發布方法。
- 可能需要其他認證: 有些網站可能需要額外的認證或權杖才能進行發布。
如果你確定 URL 和方法正確,並且網站可能需要進一步的身份驗證或特定的操作,請檢查相關的 API 文件或聯繫相關方以獲取更多幫助。
OK我觀察了header裡面
這裡面有一個Content-Type
的設定
我就把我們設定改成一致的,在header中加入 Content-Type
最終的 publish
就長這樣
def publish(id, cookie):
# 使用 get_draft_content 函數取得草稿資訊
draft_info = get_draft_content(id, cookie)
# 檢查是否成功取得草稿資訊
if not draft_info:
print(f'無法取得草稿資訊,請檢查網路連線或登入狀態')
return
# 解構草稿資訊
token = draft_info['token']
subject = draft_info['subject']
description = draft_info['description']
tags = draft_info['tags']
# 構建 URL
url = f'https://ithelp.ithome.com.tw/articles/{id}/publish'
# 構建 headers
headers = {
'Cookie': cookie,
'X-CSRF-TOKEN': token,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
# 構建傳送資料
data = {
'_token': token,
'group': 'tech',
'_method': 'PUT',
'subject': subject,
'description': description,
'tags[]': tags,
}
encoded_data = urlencode(data, doseq=True)
# 發送 POST request
response = requests.post(url, headers=headers, data=encoded_data)
if response.status_code == 200:
print('發布成功!')
else:
print(f'發布失敗,錯誤碼:{response.status_code}')
稍微運用以上function,來測試看看
userId = '你的userId'
cookie = '你的cookie'
articles_info = extract_articles_info(userId, cookie)
if len(articles_info) > 0:
id = articles_info[0]['id']
publish(id, cookie)
發文成功!
最後,由於我們程式最外側除了需要提供 cookie之外,還要提供 userId
理論上需要提供的東西越少越好,已經有登入cookie了,理論上我們應該可以拿到userId才是
(不然頁面上點擊「我的發文列表」就應該是找不到的了)
所以來觀察首頁
找到了! <a id="account">
這個裡面的href 會有userId ,而且account會有帳號名稱
其實從頭走到這裡,爬蟲取出資料的方式差不多都會了,就會直接動手寫
(有時候要描述情境也是要打不少字)
不過我們這邊是實驗用,所以繼續用嘴寫程式
你可以使用 Beautiful Soup 解析 HTML,然後選擇
<a>
元素並提取其href
屬性和data-account
屬性的值。以下是
getUser
函數的示例程式碼:from bs4 import BeautifulSoup def getUser(content): # 使用 BeautifulSoup 解析 HTML soup = BeautifulSoup(content, 'html.parser') # 找到 id 為 "account" 的 <a> 元素 a_element = soup.find('a', id='account') if a_element: # 取得 href 屬性值 href = a_element['href'] # 取得 data-account 屬性值 data_account = a_element['data-account'] # 使用正規表達式取得 id import re user_id = re.search(r'/users/(\d+)', href).group(1) return {'id': user_id, 'name': data_account} else: return None # 假設 content 是你的 HTML 字符串 # content = '<a href="/users/20135750" id="account" data-account="lalame888">我的主頁</a>' user_info = getUser(content) if user_info: print(user_info) else: print('未找到相應的 <a> 元素')
這個
getUser
函數會解析 HTML,然後找到 id 為 "account" 的<a>
元素,並提取其href
和data-account
屬性的值,然後使用正規表達式提取出用戶 ID。最後,它會返回一個包含用戶 ID 和名稱的字典。
希望這能符合你的需求!如果你有任何進一步的問題,請隨時告訴我。
OK! 我們只要改造一下,把url和吃的參數修正一下,就可以用了
def getUser(cookie):
url = 'https://ithelp.ithome.com.tw/'
content = get_url_content_with_cookie(url, cookie)
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(content, 'html.parser')
# 找到 id 為 "account" 的 <a> 元素
a_element = soup.find('a', id='account')
if a_element:
# 取得 href 屬性值
href = a_element['href']
# 取得 data-account 屬性值
data_account = a_element['data-account']
# 使用正規表達式取得 id
import re
user_id = re.search(r'/users/(\d+)', href).group(1)
return {'id': user_id, 'name': data_account}
else:
return None
達成用程式將草稿文章發表出去
輸入:登入 cookie
執行步驟:
getUser(cookie):
用cookie 取得it邦幫忙的首頁內容,去找該帳號的主頁ID與用戶名extract_articles_info(userId, cookie):
用帳號主頁ID取得草稿列表,取得草稿文章IDget_draft_content(id, cookie):
透過草稿文章ID,取得準備發文的資料publish(id, cookie):
呼叫get_draft_content
取得資料後,將文章正式發表出去真正最後成果,還是去看我另一篇文章: 【Python】鐵人賽草稿自動排程發文神器 & line notifiy
這邊有放我的gitHub與串接說明
有再加上一些選單啊、定時執行、串line notify之類的
但其實定時執行這件事情,也可以用電腦的排程去跑,每日定時開啟程式
就不用讓程式一直維持開啟狀態
這次實驗獲得的程式碼放這邊:
基本上有上面那四個function,就可以拿token做一些能夠發文的程式
使用方式可以依人而異,後面我也放了一個「啟動就直接發佈一篇最早的草稿」的簡易程式碼
import requests
from bs4 import BeautifulSoup
import re
from urllib.parse import urlencode
def get_url_content_with_cookie(url, cookie_string):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Cookie': cookie_string # 在這裡加入 cookie 字串
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print('請求成功!')
return response.text
else:
print(f'請求失敗,錯誤碼:{response.status_code}')
return None
def extract_articles_info(userId, cookie):
url = f'https://ithelp.ithome.com.tw/users/{userId}/articles'
# 使用 get_url_content_with_cookie 函數獲取 HTML 內容
content = get_url_content_with_cookie(url, cookie)
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(content, 'html.parser')
# 找出所有 class 為 "qa-list profile-list" 的 <div> 元素
divs = soup.find_all('div', class_='qa-list profile-list')
# 篩選出包含 <span class="title-badge title-badge--draft"> 的 <div> 元素
filtered_divs = [div for div in divs if div.find('span', class_='title-badge title-badge--draft') is not None]
# 初始化結果列表
result = []
# 逐一處理符合條件的 <div> 元素
for div in filtered_divs:
# 找到 <a> 元素
a_element = div.find('a', class_='qa-list__title-link')
if a_element:
# 取得 href 屬性
link = a_element['href'].strip()
# 取得內部文字
text = a_element.text.strip()
# 使用正規表達式從 link 中提取文章 ID
match = re.search(r'/articles/(\d+)', link)
if match:
article_id = match.group(1)
else:
article_id = ''
# 將結果添加到列表中
result.append({'link': link, 'text': text, 'id': article_id})
return result
def get_draft_content(id, cookie):
url = f'https://ithelp.ithome.com.tw/articles/{id}/draft'
content = get_url_content_with_cookie(url, cookie)
soup = BeautifulSoup(content, 'html.parser')
input_element = soup.find('input', {'name': '_token', 'type': 'hidden'})
if input_element:
token = input_element['value']
# 使用 BeautifulSoup 解析 HTML
subject = soup.find('input', {'name': 'subject'})['value']
description = soup.find('textarea', {'name': 'description'}).text
# 找到名為 "tags" 的 <select> 元素
select_element = soup.find('select', id='tags')
# 使用列表推導式來提取所有的 option 的 value 屬性值
tags = [option['value'] for option in select_element.find_all('option')]
return {
'token': token,
'subject': subject,
'description': description,
'tags': tags,
}
def publish(id, cookie):
# 使用 get_draft_content 函數取得草稿資訊
draft_info = get_draft_content(id, cookie)
# 檢查是否成功取得草稿資訊
if not draft_info:
print(f'無法取得草稿資訊,請檢查網路連線或登入狀態')
return
# 解構草稿資訊
token = draft_info['token']
subject = draft_info['subject']
description = draft_info['description']
tags = draft_info['tags']
# 構建 URL
url = f'https://ithelp.ithome.com.tw/articles/{id}/publish'
# 構建 headers
headers = {
'Cookie': cookie,
'X-CSRF-TOKEN': token,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
# 構建傳送資料
data = {
'_token': token,
'group': 'tech',
'_method': 'PUT',
'subject': subject,
'description': description,
'tags[]': tags,
}
encoded_data = urlencode(data, doseq=True)
# 發送 POST request
response = requests.post(url, headers=headers, data=encoded_data)
if response.status_code == 200:
print('發布成功!')
else:
print(f'發布失敗,錯誤碼:{response.status_code}')
def getUser(cookie):
url = 'https://ithelp.ithome.com.tw/'
content = get_url_content_with_cookie(url, cookie)
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(content, 'html.parser')
# 找到 id 為 "account" 的 <a> 元素
a_element = soup.find('a', id='account')
if a_element:
# 取得 href 屬性值
href = a_element['href']
# 取得 data-account 屬性值
data_account = a_element['data-account']
# 使用正規表達式取得 id
import re
user_id = re.search(r'/users/(\d+)', href).group(1)
return {'id': user_id, 'name': data_account}
else:
return None
開啟就直接發一篇最早建立的草稿範例:
user = getUser(cookie)
if user:
userId = user['id']
articles_info = extract_articles_info(userId, cookie)
if len(articles_info) > 0:
index = len(articles_info) - 1
id = articles_info[index]['id']
publish(id, cookie)
寫了一篇落落長的紀錄,本次和ChatGPT的完整對話在這邊:
https://chat.openai.com/share/687b488b-2e97-4bcb-bbb5-0e0bd8e31459
親自實驗不會ChatGPT和爬蟲到底能不能寫出python爬蟲相關程式
答案是可以的,而且花兩天就完成
不用從頭學習python,而是用我本來就會的程式底子
瘋狂詢問ChatGPT如果在python的話要怎麼寫
最初實際開發的時候,我幾乎什麼語法都要問一下
len()也不會、input也不會、else if也不會
總之當我遇到的問題,就是問,再從中去學習、吸取,再最後變成自己的知識
整個點亮前後端技能樹系列想傳達的其實就是:
ChatGPT的程式能力也許比我強,但ChatGPT並不會讓我被取代掉
好好利用與成為使用、操控工具的人,我想是不會餓肚子的
這一篇我很早就寫好放著了,一直在等我前面想講主題講完
最後剩下一篇是完結的結語,無數次地謝謝訂閱、收藏這系列文章的各位
我是不會寫python的前端工程師一宵三筵,我們明天見!