任務:批量抓取網頁pdf檔案
有一個excel,裡面有數千條指向pdf下載連結的網頁地址,現在,需要批量抓取這些網頁地址中的pdf檔案。
python環境:
anaconda3
openpyxl
beautifulsoup4
讀取excel,獲取網頁地址
使用openpyxl庫,讀取.xslx檔案;
(曾嘗試使用xlrd庫讀取.xsl檔案,但無法獲取超連結)
-
安裝openpyxl
pip install openpyxl
-
提取xslx檔案中的超連結
示例檔案構造
公告日期 | 證券程式碼 | 公告標題 |
---|---|---|
2018-04-20 | 603999.SH | 讀者傳媒:2017年年度報告 |
2018-04-28 | 603998.SH | 方盛製藥:2017年年度報告 |
def readxlsx(path):
workbook = openpyxl.load_workbook(path)
Data_sheet = workbook.get_sheet_by_name(`sheet1`)
rowNum = Data_sheet.max_row #讀取最大行數
c = 3 # 第三列是所需要提取的資料
server = `http://news.windin.com/ns/`
for row in range(1, rowNum + 1):
link = Data_sheet.cell(row=row, column=c).value
url = re.split(r`"`, link)[1]
print(url)
downEachPdf(url, server)
獲取網頁pdf下載地址
進入讀者傳媒:2017年年度報告,在chrome瀏覽器中可以按F12檢視網頁原始碼,以下擷取部分原始碼:
<div class="box4"><div style=`float:left;width:40px;background-color:#ffffff;`>附件:</div> <div style=float:left;width:660px;background-color:#f3f3f3;`> <a href=[getatt.php?id=91785868&att_id=32276645](http://news.windin.com/ns/getatt.php?id=91785868&att_id=32276645) class=`big` title=603999讀者傳媒2017年年度報告.pdf>603999讀者傳媒2017年年度報告.pdf </a> (2.00M)  </div></div>
可見,herf下載連結在a標籤中,可以通過解析html原始碼獲取下載連結。
這裡使用BeautifulSoup解析html。
Beautiful Soup 是用Python寫的一個HTML/XML的解析器,它可以很好的處理不規範標記並生成剖析樹(parse tree)。 它提供簡單又常用的導航(navigating),搜尋以及修改剖析樹的操作。它可以大大節省你的程式設計時間。
- 安裝BeautifulSoup4
pip install beautifulsoup4
- 獲取pdf下載連結並下載
def downEachPdf(target, server):
req = requests.get(url=target)
html = req.text
bf = BeautifulSoup(html, features="lxml")
a = bf.find_all(`a`)
for each in a:
url = server + each.get(`href`)
print("downloading:", each.string, url)
urllib.request.urlretrieve(url, `./report/` + each.string)
同一ip重複訪問同一伺服器被拒絕
利用以上方法已經能夠實現批量網頁pdf的下載了,但是,在實際操作過程中,會發現如果同一ip頻繁地訪問某一伺服器,訪問會被拒絕(可能被誤判為DOS攻擊,通常做了Rate-limit的網站都會停止響應一段時間,你可以Catch這個Exception,sleep一段時間,參考)。因此,對下載邏輯進行了調整。
利用try-catch,具體邏輯是:正常情況下,按次序下載檔案,如果同一檔案,下載失敗次數超過10,則跳過,下載下一個檔案,並記錄錯誤資訊。
import os
import time
def downloadXml(flag_exists, file_dir, file_name, xml_url):
if not flag_exists:
os.makedirs(file_dir)
local = os.path.join(file_dir, file_name)
try:
urllib.request.urlretrieve(xml_url, local)
except Exception as e:
print(`the first error: `, e)
cur_try = 0
total_try = 10
if cur_try < total_try:
cur_try += 1
time.sleep(15)
return downloadXml(flag_exists, file_dir, file_name, xml_url)
else:
print(`the last error: `)
with open(test_dir + `error_url.txt`, `a`) as f:
f.write(xml_url)
raise Exception(e)