Last active
December 30, 2022 08:13
-
-
Save x1001000/8cc71b54264152030ba45df753217d8f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/x1001000/8cc71b54264152030ba45df753217d8f/web_crawler.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "WcyEKCatFug3" | |
}, | |
"source": [ | |
"1. 從 [博客來 > 中文書 > 分類總覽頁](https://www.books.com.tw/web/sys_sublistb/books/?loc=subject_012) 觀察到分類範圍有大中小三層,而每個小分類網址中都有XXYYZZ,XX為第一層編號,YY為第二層編號,ZZ為第三層編號,例如 [文學小說 > 翻譯文學 > 日本文學](https://www.books.com.tw/web/sys_bbotm/books/010101) 的網址為 www.books.com.tw/web/sys_bbotm/books/010101\n", | |
"1. 設計三層巢狀迴圈,從010101開始抓資料,到010109發現過頭了,再到010201,依此類推,但這方法不好寫,不如用 [re](https://docs.python.org/3/library/re.html) 直接把分類總覽頁中有連續六個數字的網址全部找出來\n", | |
"1. 有些只有第二層沒有第三層的中分類,例如 [文學小說 > 溫馨/療癒小說](https://www.books.com.tw/web/books_bmidm_0111/?loc=P_0001_2_011) 的網址就沒有XXYYZZ,要設法另外找\n", | |
"1. 用迴圈遍訪找到的分類網址,把每個分類的書籍資料逐頁(100本/頁)彙整在一個 [pandas.DataFrame](https://oranwind.org/python-pandas-ji-chu-jiao-xue/),最後匯出一個 csv 檔\n", | |
"1. 依序執行以下 code cell 或從 Runtime 選單 Run all,全站爬完要很久,可以隨時按停止,若電腦進入休眠或離線 Runtime 會被收回,但 csv 檔們仍會在 Google Drive\n", | |
"1. 博客來網站會對持續讀取網頁的連線停止回應,若爬蟲繼續嘗試連線則會被封鎖,必須重置 Runtime 換 IP 才能解決,經實測發現,博客來網站停止回應時,讓爬蟲裝死一分鐘,就能重新得到回應" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "bkrYZIoPipdn", | |
"outputId": "d1bd8dbd-e3bc-472d-9feb-6db40bf8da8a" | |
}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"name": "stdout", | |
"text": [ | |
"Mounted at /content/drive\n" | |
] | |
} | |
], | |
"source": [ | |
"# 掛載 Google Drive 於目前 Runtime 下,程式匯出的 csv 檔們就可以存進 Google Drive,再用 Google Sheets 開啟\n", | |
"from google.colab import drive\n", | |
"drive.mount('/content/drive')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"id": "7WY79UqlxNaC" | |
}, | |
"outputs": [], | |
"source": [ | |
"# 下 Linux 指令在 Google Drive 新增資料夾\n", | |
"! mkdir drive/MyDrive/博客來-中文書" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"id": "SGu0J95d0Vg4" | |
}, | |
"outputs": [], | |
"source": [ | |
"# 匯入所需的 Python 套件\n", | |
"import requests\n", | |
"from bs4 import BeautifulSoup\n", | |
"import re\n", | |
"import pandas as pd\n", | |
"from time import sleep" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "hFgwlAz40oYx", | |
"outputId": "cfb10f6a-b9b1-4f0f-edab-5ef2d8d16d7d" | |
}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"name": "stdout", | |
"text": [ | |
"找到945個小分類網址,另外找到64個沒有小分類的中分類網址,皆儲存於urls中\n" | |
] | |
} | |
], | |
"source": [ | |
"# 找出全部博客來中文書的分類網址\n", | |
"r = requests.get('https://www.books.com.tw/web/sys_sublistb/books/?loc=subject_012')\n", | |
"urls = []\n", | |
"soup = BeautifulSoup(r.text)\n", | |
"for link in soup.find_all('a'):\n", | |
" url = link.get('href')\n", | |
" if re.search(r'/\\d\\d\\d\\d\\d\\d/', url):\n", | |
" urls.append(url)\n", | |
"n = len(urls)\n", | |
"for mid in r.text.split('</th>'):\n", | |
" if '<ul></ul>' in mid:\n", | |
" urls.append(BeautifulSoup(mid).find('a').get('href'))\n", | |
"print(f'找到{n}個小分類網址,另外找到{len(urls)-n}個沒有小分類的中分類網址,皆儲存於urls中')\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"colab": { | |
"base_uri": "https://localhost:8080/" | |
}, | |
"id": "g9DiHTgCVJU3", | |
"outputId": "444f63cc-dad0-4fe0-a005-19357037f505" | |
}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"name": "stdout", | |
"text": [ | |
"博客來-中文書>文學小說>翻譯文學>日本文學 Page:1.2.3.4.5.6.7.8.9.10.11.12.13.14.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>韓國文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>亞洲文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>美國文學 Page:1.2.3.4.5.6.7.8.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>南美文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>英國文學 Page:1.2.3.4.5.✅\n", | |
"(暫停一分鐘)\n", | |
"博客來-中文書>文學小說>翻譯文學>德國文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>法國文學 Page:1.2.3.✅\n", | |
"博客來-中文書>文學小說>翻譯文學>其他地區 Page:1.2.3.4.✅\n", | |
"博客來-中文書>文學小說>華文創作>散文 Page:1.2.3.4.5.6.7.8.9.10.11.12.13.14.✅\n", | |
"博客來-中文書>文學小說>華文創作>小說 Page:1.2.3.4.5.6.7.8.9.10.(暫停一分鐘).(暫停一分鐘).11.✅\n", | |
"博客來-中文書>文學小說>華文創作>飲食文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>華文創作>旅遊文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>華文創作>自然書寫 Page:1.✅\n", | |
"博客來-中文書>文學小說>華文創作>報導文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>詩>華文現代詩 Page:1.2.3.4.5.✅\n", | |
"博客來-中文書>文學小說>詩>外國詩 Page:1.2.✅\n", | |
"博客來-中文書>文學小說>文學研究>華文文學研究 Page:1.2.3.4.5.✅\n", | |
"博客來-中文書>文學小說>文學研究>外國文學研究 Page:1.2.✅\n", | |
"博客來-中文書>文學小說>文學研究>文學史 Page:1.✅\n", | |
"博客來-中文書>文學小說>文學研究>作家傳記 Page:1.✅\n", | |
"博客來-中文書>文學小說>中國古典文學>經史子集 Page:1.✅\n", | |
"博客來-中文書>文學小說>中國古典文學>古典小說 Page:1.(暫停一分鐘).2.✅\n", | |
"博客來-中文書>文學小說>中國古典文學>古典文學 Page:1.2.✅\n", | |
"博客來-中文書>文學小說>中國古典文學>詩詞曲賦 Page:1.2.3.✅\n", | |
"博客來-中文書>文學小說>國學常識>作文、創作 Page:1.✅\n", | |
"博客來-中文書>文學小說>國學常識>字詞分析 Page:1.✅\n", | |
"博客來-中文書>文學小說>世界經典文學>神話、傳說 Page:1.✅\n", | |
"博客來-中文書>文學小說>世界經典文學>歐美經典文學 Page:1.2.3.4.✅\n", | |
"博客來-中文書>文學小說>世界經典文學>日本古典文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>世界經典文學>其他經典文學 Page:1.✅\n", | |
"博客來-中文書>文學小說>懸疑、推理小說>歐美懸疑、推理小說 Page:1.2.3.4.5.✅\n", | |
"博客來-中文書>文學小說>懸疑、推理小說>日本懸疑、推理小說 Page:1.2.3.4.5.(暫停一分鐘).6.✅\n", | |
"博客來-中文書>文學小說>懸疑、推理小說>華文懸疑、推理小說 Page:1.2.3.✅\n", | |
"博客來-中文書>文學小說>科幻、奇幻小說>歐美科幻、奇幻小說 Page:1.2.3.4.5.✅\n", | |
"博客來-中文書>文學小說>科幻、奇幻小說>日本科幻、奇幻小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>科幻、奇幻小說>其他科幻、奇幻小說 Page:1.2.3.4.5.6.7.✅\n", | |
"博客來-中文書>文學小說>恐怖、驚悚小說>歐美恐怖、驚悚小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>恐怖、驚悚小說>日本恐怖、驚悚小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>恐怖、驚悚小說>其他恐怖、驚悚小說 Page:1.2.3.4.✅\n", | |
"博客來-中文書>文學小說>羅曼史、言情小說>華文羅曼史 Page:1.2.3.4.(暫停一分鐘).5.6.7.8.9.10.11.12.13.14.15.16.✅\n", | |
"博客來-中文書>文學小說>羅曼史、言情小說>外國羅曼史 Page:1.✅\n", | |
"博客來-中文書>文學小說>歷史、武俠小說>華文歷史小說 Page:1.2.3.✅\n", | |
"博客來-中文書>文學小說>歷史、武俠小說>日本歷史小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>歷史、武俠小說>外國歷史小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>歷史、武俠小說>金庸武俠小說 Page:1.✅\n", | |
"博客來-中文書>文學小說>歷史、武俠小說>其他武俠小說 Page:1.2.3.✅\n", | |
"博客來-中文書>商業理財>傳記>人物傳記 Page:1.2.✅\n", | |
"博客來-中文書>商業理財>傳記>企業傳記 Page:1.✅\n", | |
"博客來-中文書>商業理財>管理與領導>管理學 Page:1.✅\n", | |
"博客來-中文書>商業理財>管理與領導>組織/管理 Page:(暫停一分鐘).1.2.3.✅\n", | |
"博客來-中文書>商業理財>管理與領導>領導/帶人 Page:1.2.✅\n", | |
"博客來-中文書>商業理財>管理與領導>經營策略 Page:1.2.3.✅\n", | |
"博客來-中文書>商業理財>管理與領導>創業開店 Page:1.2.✅\n", | |
"博客來-中文書>商業理財>管理與領導>各行各業經營 Page:1.2.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>專案管理 Page:1.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>人力資源管理 Page:1.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>財務管理 Page:1.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>資訊管理 Page:1.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>生產/作業管理 Page:1.✅\n", | |
"博客來-中文書>商業理財>專業管理實務>品質管理 Page:" | |
] | |
} | |
], | |
"source": [ | |
"# 遍訪找到的分類網址,逐頁彙整書籍資料,DataFrame 轉 csv,存至 Google Drive\n", | |
"for url in urls:\n", | |
" while True:\n", | |
" r = requests.get(url)\n", | |
" if r.status_code == 200:\n", | |
" soup = BeautifulSoup(r.text)\n", | |
" filename = soup.title.string.replace('/', '、')\n", | |
" print(filename, end=' Page:')\n", | |
" df = pd.DataFrame(columns=['書名', '作者', '出版日期', '內容介紹', '網址'])\n", | |
" p = 1\n", | |
" break\n", | |
" else:\n", | |
" print('(暫停一分鐘)')\n", | |
" sleep(60)\n", | |
" while True:\n", | |
" r = requests.get(url+f'&o=5&page={p}') # 排序依暢銷度&跳到p頁\n", | |
" if r.status_code == 200:\n", | |
" soup = BeautifulSoup(r.text)\n", | |
" print(p, end='.')\n", | |
" p += 1\n", | |
" books = soup.find_all(class_ = 'item')\n", | |
" for book in books:\n", | |
" book_info = book.find(class_ = 'info')\n", | |
" if book_info:\n", | |
" df.loc[len(df)+1] = None\n", | |
" df.iloc[-1]['書名'] = book.find('h4').text\n", | |
" df.iloc[-1]['作者'] = book_info.text.split(',')[0]\n", | |
" df.iloc[-1]['出版日期'] = book_info.text.split(',')[-1].split(':')[-1]\n", | |
" df.iloc[-1]['內容介紹'] = book.find(class_ = 'txt_cont').text\n", | |
" df.iloc[-1]['網址'] = book.find('a').get('href')\n", | |
" if len(df) < 100 or '共有<em>100</em>本' in r.text: # 分類少於100本或等於100本\n", | |
" break\n", | |
" elif r.status_code == 404: # 超過頁數\n", | |
" break\n", | |
" else:\n", | |
" print('(暫停一分鐘)', end='.')\n", | |
" sleep(60)\n", | |
" df.to_csv('drive/My Drive/博客來-中文書/'+filename+'.csv')\n", | |
" print('✅')\n" | |
] | |
} | |
], | |
"metadata": { | |
"colab": { | |
"provenance": [], | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"display_name": "Python 3", | |
"name": "python3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment