有些時(shí)候我們需要查看一些數(shù)據(jù)報(bào)告偏塞,而某些網(wǎng)站又提供下載地址府瞄,但是分頁(yè)又比較多廉赔,一頁(yè)一頁(yè)下載太花時(shí)間青抛,所以寫個(gè)爬蟲批量將所有PDF文件下載下來(lái)旗闽。接下來(lái)將從Talkdata上批量下載所有的PDF數(shù)據(jù)報(bào)告。
分析網(wǎng)址
首先蜜另,找到Talkdata數(shù)據(jù)報(bào)告下載地址:http://mi.talkingdata.com/reports.html适室,從頁(yè)面上可以看到左側(cè)有報(bào)告的分類,中部則是報(bào)告地址举瑰,而底部有分頁(yè)按鈕捣辆。這里需要先獲取分類的鏈接地址,然后逐頁(yè)獲取報(bào)告地址此迅,再通過(guò)報(bào)告下載地址下載PDF文件汽畴。通過(guò)查看發(fā)現(xiàn)“提交并下載此報(bào)告”按鈕含有報(bào)告下載地址,不需要經(jīng)過(guò)跳轉(zhuǎn)耸序,但前端頁(yè)面正常下載則需提交姓名忍些、公司、郵箱及手機(jī)號(hào)才可點(diǎn)擊提交坎怪。
構(gòu)建爬蟲
獲取所有的分類地址
先請(qǐng)求數(shù)據(jù)報(bào)告頁(yè)面罢坝,解析獲得分類鏈接地址,并過(guò)濾掉“全部資源”的鏈接避免重復(fù)搅窿。前面先導(dǎo)入所有需要使用到的相關(guān)庫(kù)嘁酿,request
用于請(qǐng)求URL頁(yè)面,time
用來(lái)延時(shí)避免訪問(wèn)過(guò)快男应,urllib.request
用于下載PDF文件闹司,BeautifulSoup
解析文本,count
構(gòu)建迭代器殉了。get_category_urls
最終返回存放所有分類鏈接地址的列表开仰。
import requests
import time
import urllib.request
from bs4 import BeautifulSoup
from itertools import count
def get_category_urls():
"""獲取所有的分類鏈接"""
category_urls = [] # 存放分類鏈接地址
url = 'http://mi.talkingdata.com/reports.html'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
categorys = soup.select('div.report-left > ul > li > a') # 獲取分類標(biāo)簽元素
for category in categorys:
category_name = category['title'] # 分類名稱
category_url = category['href'] # 分類鏈接
# 排除“全部”
if category_url.split('=')[-1] != 'all':
category_urls.append(category_url)
return category_urls
獲取所有的報(bào)告地址
因?yàn)槎喾诸惡投囗?yè)的原因,這里為了使爬蟲更加清晰簡(jiǎn)單,將使用兩個(gè)函數(shù)來(lái)獲取所有的報(bào)告地址众弓。先構(gòu)獲取單一分類下所有頁(yè)碼的報(bào)告地址恩溅,再獲取每一個(gè)分類下的所有報(bào)告地址。get_category_pages
函數(shù)將從第一頁(yè)開(kāi)始遍歷谓娃,直到當(dāng)頁(yè)面鏈接下獲取的報(bào)告地址元素列表為空的時(shí)候斷開(kāi)循環(huán)脚乡。在每次循環(huán)獲取頁(yè)碼頁(yè)面的地方添加time.sleep
進(jìn)行延時(shí)操作。get_report_urls
函數(shù)遍歷分類鏈接列表滨达,傳入get_category_pages
奶稠,最后添加到存在所有報(bào)告鏈接的列表中。
def get_report_urls(category_urls):
""""獲取所有分類下的報(bào)告鏈接"""
all_report_urls = [] # 存放所有報(bào)告URL地址
for category_url in category_urls:
category_report_urls = get_category_pages(category_url)
all_report_urls.extend(category_report_urls)
return all_report_urls
def get_category_pages(category_url):
"""獲取一個(gè)分類下所有頁(yè)碼的報(bào)告鏈接"""
category_report_urls = [] # 存放分類下所有頁(yè)碼的報(bào)告鏈接
# 從第一頁(yè)開(kāi)始遍歷捡遍,當(dāng)獲取的報(bào)告列表為空時(shí)锌订,即最后一頁(yè)之后停止
for page in count(1):
start_url = category_url + '&tag=all&page=' + str(page)
response = requests.get(start_url)
soup = BeautifulSoup(response.text, 'lxml')
books = soup.select('div.operate-book > em > a')
if books:
for book in books:
report_name = book['title'] # 報(bào)告名稱
report_url = book['href'] # 報(bào)告鏈接
category_report_urls.append(report_url)
else:
break
time.sleep(2)
下載報(bào)告PDF文件
在獲取所有的報(bào)告鏈接地址后,需要從報(bào)告頁(yè)面中提取報(bào)告下載地址画株。因下載鏈接地址含有中文辆飘,所以需要使用urllib.request.quote
對(duì)URL進(jìn)行編碼操作。到了這里基本已經(jīng)完成爬蟲程序谓传,但是還沒(méi)結(jié)束蜈项。
def download_report(report_url):
"""根據(jù)報(bào)告地址下載PDF文件"""
response = requests.get(report_url)
soup = BeautifulSoup(response.text, 'lxml')
download_link = soup.find('button')['data-url'] # 獲取報(bào)告下載地址
file_name = download_link.split('/')[-1] # PDF文件名
urllib.request.urlretrieve('https://' + urllib.request.quote(download_link[8:]), '{}.pdf'.format(file_name))
爬蟲主程序
前面已經(jīng)寫好所有相關(guān)的爬蟲函數(shù),最后添加爬蟲主程序spider
對(duì)爬蟲函數(shù)進(jìn)行調(diào)用续挟,遍歷所有的下載鏈接地址紧卒,調(diào)用download_report
進(jìn)行下載,這使得整個(gè)過(guò)程更加清晰明了诗祸。運(yùn)行主程序后等待幾十分鐘跑芳,所有PDF文件均已下載成功。
def spider():
"""爬蟲主程序"""
category_urls = get_category_urls()
report_urls = get_report_urls(category_urls)
for report_url in report_urls:
download_report(report_url)
time.sleep(2)
if __name__ == '__main__':
spider()