はじめに
これが、ChatGPTとのペアプロ...! nikkieです
Webページの本文を取得するというめちゃめちゃステキ✨な関数の実装を見かけました。
「なにこれ、めっちゃ便利!私も使いたい!!」
その実装はJavaScriptです。
「Pythonでおんなじことできないかな〜」と考えた私は、ChatGPTの力を借りることにしました。
目次
経緯
このブログで何回か取り上げているこちらの記事。
ChatGPTでできることとして、要約も挙がっています。
要約の実装を見たところ、URLから本文を抽出していました。
export async function extractArticleContent(url: string): Promise<string> { const dom = await JSDOM.fromURL(url); const reader = new Readability(dom.window.document); const article = reader.parse(); const content = new dom.window.DOMParser() .parseFromString(article.content, "text/html") .body .textContent || ""; return content; }
URLから本文抽出、これは私がかなり欲しかった実装です。
「JavaScriptの実装をPythonに書き換えられるのでは」と、プログラミング言語間の書き換えで(私の中で)定評のある1ChatGPTの力を借りることにしました。
結論(暫定解)
以下のスクリプトで本文が取得できます。
% python extract_body.py https://www.itmedia.co.jp/business/articles/2303/14/news050.html 仕事に役立つ調査データ: (以下略)
動作環境
- Python 3.10.9
- requests 2.28.2
- readability-lxml 0.8.1
- beautifulsoup4 4.12.2
- chardet 5.1.0
ChatGPTとのペアプロ
初手「JavaScriptの実装、解説して」🙏
先のJavaScriptの実装を解説してもらうところから始めます。
関数の主要部分を抜き出しました(※JavaScript素人目に主要と判断した部分です)
>>>以下のJavaScriptのコードを解説して私を助けてください const dom = await JSDOM.fromURL(url); const reader = new Readability(dom.window.document); const article = reader.parse(); const content = new dom.window.DOMParser() .parseFromString(article.content, "text/html") .body .textContent || "";
丁寧な解説に定評のあるChatGPT氏。
各行を解説してくれました。
ここで知ったのは2つのライブラリを使っているということ
今回取り上げた実装ができることは、@mozilla/readability
の https://github.com/mozilla/readability/tree/0.4.4#nodejs-usage に書いてあるんですね!
Pythonのライブラリreadability-lxmlを知った!
Pythonでどうやるかを聞いたところ、readability-lxml
というライブラリを教えてもらいます。
>>>なるほど。私はJavaScriptよりPythonが得意なのですが、Pythonに書き換えたらどうなるのか教えてほしいです
Pythonで同じ処理を行う場合は、以下のようなコードになります。
import requests from readability import Document url = "https://example.com" response = requests.get(url) doc = Document(response.text) content = doc.summary()
(※続く解説は省略します)
生成されたこのコード、ライブラリのREADMEのサンプルコードと同様なんですよ!
from readability import Document
の行を書くにはライブラリのインストールが必要だと思いますが、「readability」で検索しても見つからず。
pip install readability
でいいのかなと思い質問。
>>>Pythonのreadabilityライブラリはどのようにインストールすればいいのでしょうか
Pythonのreadabilityライブラリは、Pythonのパッケージ管理ツールであるpipを使ってインストールすることができます。以下のコマンドをターミナルで実行してください。 pip install readability-lxml これで、readabilityライブラリがインストールされ、Pythonで利用することができるようになります。
PyPIでも裏が取れ、無事インストールできました。
requests -> readability-lxml -> beautifulsoup4
先のPythonを動かすと、contentにはHTMLタグを含んだ文字列が取得されています(ページ本文部分なの、すごい!)。
「HTMLタグをどう除けばいいかな、BeautifulSoupとかでいけたよな」と思いつつ聞いてみます。
>>>doc.summary()が返す値はHTMLタグをもった文字列でした。 '<html><body><div><body id="readabilityBody">\n<div>\n <h1>Example Domain</h1>\n HTMLタグを除くにはどんな方法があるでしょうか
doc.summary()が返す値は、HTMLタグを含んだ文字列であるため、HTMLタグを除去するためには、Pythonのbeautifulsoup4ライブラリを使うことができます。 以下のように、beautifulsoup4ライブラリを使って、HTMLタグを取り除いた文字列を得ることができます。
import requests from readability import Document from bs4 import BeautifulSoup url = "https://example.com" response = requests.get(url) doc = Document(response.text) # HTMLタグを取り除く soup = BeautifulSoup(doc.summary(), "html.parser") text = soup.get_text() print(text)
おお、やはりBeautifulSoup
!
そうか、get_text
で全てのHTMLタグがstripされるのか!
https://www.crummy.com/software/BeautifulSoup/bs4/doc/#get-text
If you only want the human-readable text inside a document or tag, you can use the get_text() method.
文字コード!(文字化け😱)
ここまででほしいスクリプトはできたので、参考記事と同じURLを対象に動かしてみます。
ChatGPT、使用している経営者は1割未満 「調べものに活用」「質の低いプログラマー淘汰」の声も
すると、文字化け!
dÉð§Â²¸f[^F
対象ページのソース(HTML)を見てみると、charsetがshift_jisなのです(ここを意識しないで済むJavaScript実装、すごすぎますね)。
>>>ありがとうございます。たいへん助かっています。HTMLのmetaタグでcharset=shift_jisだと文字化しちゃうんですが、どんな対策が考えられますか?
(感想戦:ここでもっと具体を与えるとよかったかも)
shift_jisは日本語の文字コードであり、日本語のWebページでは比較的よく使われますが、文字コードがshift_jisであると、日本語以外の文字が正しく表示されない可能性があります。 この問題を解決するには、以下のような対策が考えられます。 1. requestsライブラリを使ってWebページの文字コードを明示的に指定する 2. HTMLのmetaタグから文字コードを取得し、それに基づいて文字コードを変換する 具体的には、以下のようなコードを書くことができます。
import requests from readability import Document from bs4 import BeautifulSoup import chardet url = "https://example.com" # Webページの文字コードを明示的に指定する response = requests.get(url, headers={"Content-Type": "text/html; charset=utf-8"}) # 文字コードを取得する soup = BeautifulSoup(response.content, "html.parser") meta_charset = soup.find("meta", attrs={"charset": True}) if meta_charset: charset = meta_charset["charset"] else: # chardetライブラリを使って文字コードを自動判別する charset = chardet.detect(response.content)["encoding"] # 文字コードを変換する doc = Document(response.content.decode(charset)) summary = doc.summary() # HTMLタグを取り除く soup = BeautifulSoup(summary, "html.parser") text = soup.get_text() print(text)
いくつか教えてくれました
requests.get
するときにheaders
を使ってcharsetを指定- IMO:これって機能するんです? ちょっと試して無理筋っぽかったので見送り
BeautifulSoup
でmeta
タグを見つけて、charsetを取得chardet
を使ってcharsetを自動判別
2か3でcharsetが分かったら、それを指定してページのコンテンツをdecode
すればいいわけですね!2
chardetによる文字コード自動判定に至る
ライブラリを追加したくなかったので、2のmeta
タグを見つける路線で進めます。
>>>なるほど、こんな方法が! meta_charset = soup.find("meta", attrs={"charset": True}) で見つからないみたいなのですが、個々の解説お願いします
(「個々」は変換ミスです)
metaタグには、charset属性が指定されていることがあります。この属性には、Webページの文字コードが指定されています。 BeautifulSoupライブラリを使ってmetaタグを検索するには、find()メソッドを使います。このメソッドは、指定した属性が存在する最初の要素を返します。 以下のように、find()メソッドにattrsパラメータを指定することで、metaタグの属性に対する検索を行うことができます。 python meta_charset = soup.find("meta", attrs={"charset": True}) attrsパラメータには、charset属性が存在することを表す{"charset": True}を指定しています。 ただし、すべてのWebページでcharset属性が指定されているわけではないため、この方法だけでは文字化けが解決できない場合があります。その場合は、chardetライブラリを使って自動判別するなど、別の方法を試す必要があります。
>>><meta http-equiv="content-type" content="text/html;charset=shift_jis"> からcharsetを取得するにはどうすればいいでしょうか
metaタグのhttp-equiv属性には、content-typeが指定されていることがあります。この場合、文字コードはcontent属性に含まれています。 以下のように、http-equiv属性がcontent-typeであるmetaタグを検索し、content属性の値から文字コードを取得することができます。
meta_content_type = soup.find("meta", attrs={"http-equiv": "content-type"}) if meta_content_type: content = meta_content_type["content"] charset_pos = content.find("charset=") if charset_pos >= 0: charset = content[charset_pos + 8:].lower()
find()メソッドを使って、charset=という文字列が含まれている位置を検索し、その後ろに続く文字列を切り出しています。また、取得した文字コードは小文字に変換しています。
ここまで聞いて、2の路線はけっこう処理を書かないといけずめんどくさいなと感じ、3のchardetに切り替えました。
依存は増えますが、1行だけで済みます!
charset = chardet.detect(response.content)["encoding"]
こうして文字化けにも対処できました!
終わりに
URLから本文抽出するJavaScriptの実装(とても欲しかったもの)を見つけ、powered by ChatGPTでPythonに書き直しました。
HTMLから本文を取り出してくれるreadability-lxml
、未来ずら〜。
そしてchardet
を使って、文字コードがutf-8以外でも対応できるように!
ChatGPT、ありがとう〜
「ぴょんぴょん」会話する3だけじゃなく、サンプルコードを錬成してくれるのマジで便利ですね、加速する!
P.S. ChatGPT関連勉強会紹介ゾーン!
「ChatGPTとのペアプロ」は id:karaage さんの記事で事例を知っていました。
この事例でからあげさんは「ChatGPT チャレンジ」コンペ制覇、おめでとうございます!
4/28(金)にナレッジ共有会!
また、TypeScriptではじめるChatGPT API:入門から一歩進んだテクニックまで - Uzabase for Engineers関連では、4/27(木)に勉強会があります!
エントリの執筆者の中村さんも登壇です。
LTの数がヤバい!
今週は4/25(火)にMLOpsコミュニティでもLT大会があったりと、毎日のようにChatGPTの事例共有ありますね、大盛り上がり!