序文
最近、小説を読むのが好きな友人が増えていますが、毎回特定のサイトにアクセスして読むのは非常に面倒です。特に田舎の故郷に帰ると電波が悪くてデータネットワークが使えないことがありますが、WIFIはありますか?なので、小説を事前にダウンロードできればOKです。
準備する
実は、準備するものは何もありません。クローラーがよく使用するモジュールは問題ありません。現在使用されているモジュールは、request、json、re の 3 つだけです。
トピックを開始します
パケットをキャプチャする
実際、クローラーのステップは非常に固定されています。最初のステップは、パッケージを手に取り、小説をランダムに選択し、無料トライアルをクリックすることです。小説の内容が表示されます。ディレクトリをクリックすると、アドレス バーのアドレスが変更されていないことがわかります。そのため、Ajax によって要求され、Fetch/XHR でデータ パケットを直接検索します。
予想通り、全章のjsonデータは以下のデータパッケージ内にありました。
レイヤーごとに展開し、各インデックスの下でこれらのコンテンツを見つけます。
次に、これらのデータと各チャプターの関係を調べますが、3 つのチャプターのアドレスバーを観察すると、以前作成したミュージック クローラーとまったく同じパターンが簡単に見つかりました。
第2章
第三章
第四章
このアドレスの前の文字はすべて変更されておらず、変更されたのは最後のデータだけであることは誰でも簡単にわかります。このデータは、注意深く観察すると、上記のデータ パケットのチャプター インデックスの下にある id に対応する値であることが簡単にわかります。このようにして、各章のアドレスが解決されます。
次に、各章の詳細ページに移動して、この小説のテキスト コンテンツを取得する方法を見つける必要があります。
「すべて」の最初のパケットに小説のテキストコンテンツを見つけました
この時点で、立ち止まって振り返ることができます。これまでにどのような有益な情報が得られましたか?
1.各章のアドレスはURLです
2. 各章の詳細ページの小説テキストの内容はどこにありますか?
次に、この URL に基づいて小説のテキスト コンテンツを取得するだけです。
タイプコード
まず、目次ページのデータパケットを要求し、目次ページのデータパケットを通じて各章のURLを取得します。ここで注意が必要なのは、このウェブサイトはある程度のクロール対策が施されており、リクエストヘッダーを持っていない場合、データをリクエストすることができません。
リクエストURLとリクエストヘッダーは上図のようになり、データをリクエストすることができます。bookIdに注目してください。使用する場合は、このbookIdを直接入力すれば正常に動作します。先ほど示した各章URLの最後から2番目のデータ、つまり最後から2番目の数字の列です。それが小説IDです。もう 1 つの点は、実際にはリクエスト ヘッダーに暗号化されたフィールドが多数あることです。これらのフィールドが追加されるとデータはリクエストされなくなります。スクリーンショットは撮りませんでした。リクエストコードは以下のとおりです。
bookId = input('bookId:')
url = 'https://www.qidian.com/ajax/book/category?bookId={}&_csrfToken=mLzoQhBuJ9pPDdAm8VpBuvMFXCgQQAlhFrC9TuvU'.format(bookId)
headers = {
'Accept':'application/json, text/plain, */*',
'Accept-Encoding':'gzip, deflate, br',
'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',
'Cookie':'_csrfToken=mLzoQhBuJ9pPDdAm8VpBuvMFXCgQQAlhFrC9TuvU; newstatisticUUID=1692151465_166373733; Hm_lvt_f00f67093ce2f38f215010b699629083=1692151469; fu=1381548093; _yep_uuid=ad752dda-9748-ea50-e98f-865f3b8bb989; _gid=GA1.2.1689446406.1692151469; supportwebp=true; supportWebp=true; trkf=1; qdrs=0%7C3%7C0%7C0%7C1; navWelfareTime=1692152350314; showSectionCommentGuide=1; qdgd=1; rcr=1036370336; bc=1036370336; e1=%7B%22pid%22%3A%22qd_P_mycenter%22%2C%22eid%22%3A%22qd_H_mall_bottomaddownload%22%2C%22l7%22%3A%22hddl%22%7D; e2=%7B%22l6%22%3A%22%22%2C%22pid%22%3A%22qd_p_qidian%22%2C%22eid%22%3A%22qd_A16%22%2C%22l1%22%3A3%7D; lrbc=1036370336%7C749524263%7C0; _gat_gtag_UA_199934072_2=1; traffic_utm_referer=https%3A%2F%2Flink.csdn.net%2F; Hm_lpvt_f00f67093ce2f38f215010b699629083=1692155018; _ga_FZMMH98S83=GS1.1.1692151469.1.1.1692155018.0.0.0; _ga_PFYW0QLV3P=GS1.1.1692151469.1.1.1692155018.0.0.0; _ga=GA1.2.2023067070.1692151469',
'Host':'www.qidian.com',
'Referer':'https://www.qidian.com/chapter/1023826840/573073457/',
'sec-ch-ua':'"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
'sec-ch-ua-mobile':'?0',
'sec-ch-ua-platform':'"Windows"',
'Sec-Fetch-Dest':'empty',
'Sec-Fetch-Mode':'cors',
'Sec-Fetch-Site':'same-origin',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42',
'X-D':'0'
}
response = requests.get(url=url, headers=headers)
もう一つ、テキストを直接出力すると文字化けが起こるため、エンコード形式を手動でutf-8に設定しています。
response.encoding = 'utf-8'
次に、正規表現を使用してテキスト内の ID 値と、もちろん各章の名前を抽出します。
ex = '{"uuid":.*?,"cN":"(.*?)","uT":.*?,"cnt":.*?,"cU":.*?,"id":(.*?),"sS":.*?}'
data = re.findall(ex, response.text, re.S)
この時点で、各章の URL と各章の名前がわかります。
データ抽出
次に章ごとにデータを抽出していきます。もちろん、最初のステップは各章の URL を作成し、次に各章の URL をリクエストすることです。次に、正規表現を使用して新規テキストを抽出し続けます。コードは次のとおりです。
chapterUrlList = []
titleList = []
bookBodyList = []
print("正在获取书籍内容,请耐心等待哦!")
for i in data:
# 获取每一章的章名
title = i[0]
titleList.append(title)
# 获取每一章的链接地址
chapterUrl = 'https://www.qidian.com/chapter/{}/'.format(bookId) + i[-1] + '/'
chapterUrlList.append(chapterUrl)
# 用正则表达式获取每一章的内容
book = requests.get(url = chapterUrl)
ex1 = '<main .*?><p>(.*?)</p></main>'
bookBody = re.findall(ex1, book.text, re.S)
# 用replace替换掉小说文本中的一些特殊符号
body = bookBody[0].replace('\u3000\u3000', '\n')
body = body.replace('<p>', '')
body = body.replace('</p>', '')
# 将文本保存到列表中
bookBodyList.append(body)
# 获取小说名字和小说作者等信息
book_Info = requests.get(url = chapterUrl)
ex2 = '<script id="vite-plugin-ssr_pageContext" type="application/json">(.*?)</script>'
book_Info = re.findall(ex2, book_Info.text, re.S)[0]
json_data = json.loads(book_Info)
print(type(json_data))
bookName = json_data['pageContext']['pageProps']['pageData']['bookInfo']['bookName']
authorName = json_data['pageContext']['pageProps']['pageData']['bookInfo']['authorName']
print("正在保存,马上就好哦!")
最後のステップは、要求された小説テキストを保存することです。
with open('书名:'+bookName + '作者' + authorName + '.txt', 'w') as f:
for j in range(0, len(titleList)):
f.write(titleList[j])
f.write(':')
f.write(chapterUrlList[j])
f.write(bookBodyList[j])
print("保存成功!")
これで完了です!
予防
1. Webサイトはある程度逆クロールされていますが、リクエストヘッダーに暗号化されたパラメータがいくつかあり、すべて追加されている場合はデータのリクエストができず、何も追加されていない場合はデータのリクエストができません。
2. 実は、最も面倒な点はデータの抽出であり、新規テキストコンテンツの中には HTML 構文記号が多く含まれているものがあるため、それらをクリーンアップする必要があります。
3. 正規表現は実際には一致しにくい、あるいは私の学習能力が低いのかもしれません。レスポンスからHTMLの標準コードを直接コピーしているため、改行など表示できない記号が含まれています。マッチングする場合、これらの制限された文字はマッチングできません。
完全なコードをリソースに掲載しますので、ご興味があればダウンロードしてください。
学習目的のみに使用し、違法な目的で使用しないでください。