今天是2016年6月9日访惜,也是農(nóng)歷的端午節(jié)。
難得的小長假萍虽,轉(zhuǎn)眼就到學期末,忙了一學期大概大家或回家過端午或在宿舍休息調(diào)養(yǎng)吧形真,8點25到實驗室的時候只有一個從廣西大學來學院進修的教授已經(jīng)開始了一天的工作杉编。自己是覺得沒什么特別,所有的節(jié)日能讓人開心就好咆霜,即使沒有粽子吃邓馒。
Anyway, 還是想說一聲:端午節(jié)快樂~~
最近實習還一直沒有著落蛾坯,周一的面試是已經(jīng)gg了光酣,然后又開始投簡歷以求暑假有個去處,而不至于落單無事可做脉课,盡管自己口頭上一直說著無所謂救军。
這次的任務是下載上海清算網(wǎng)( http://www.shclearing.com/xxpl/fxpl/ )上前五頁中的pdf附件。自己大約琢磨了兩天才搞定倘零,最開始以為可以像下載常規(guī)的pdf文件文件一樣缤言,直接獲得pdf文件的鏈接地址然后用retrieve函數(shù)即可完成,但是發(fā)現(xiàn)pdf文件的路徑是有javascript腳本控制的视事,你根本拿不到文件的鏈接地址胆萧。
于是開始看能不能構(gòu)造路徑,發(fā)現(xiàn)還是行不通俐东,再用fiddler去監(jiān)控點擊下載時發(fā)生了什么跌穗,這時才有了一點眉目:在點擊pdf下載時,是有提交post請求的虏辫,傳入的了兩個參數(shù)分別是filename和descname蚌吸。這時才恍然大悟,像這種點擊下載的文件砌庄,你要模擬這個點擊行為羹唠,一般應該就是一個post請求。
再說一下知道是提交post請求后實際操作時有幾點要注意:
1.提交到post請求中的參數(shù)娄昆,通過查看源碼后發(fā)現(xiàn)是可以用BesutifulSoup配合正則表達式從源碼中來直接提取的佩微,正則表達式一般就用re.search(),或者re.findall()
2.提交的filename和descname參數(shù)必須先轉(zhuǎn)成URL編碼萌焰,這是通過fiddler截取到的form表單中發(fā)現(xiàn)的哺眯,所以又引入urllib庫中的parse,再使用parse.quote(text)函數(shù)來實現(xiàn)將兩個參數(shù)轉(zhuǎn)化為URL編碼扒俯。(自己操作時quote()函數(shù)的用法是采用的python3中的語法奶卓,若是在python2.x版本中則是直接用urllib.quote(text)即可一疯,但是,但是python2.x版本關(guān)于中文編碼真的有很多問題夺姑,動不動就報錯墩邀,所以果斷沒有用python2.x版本)
3.在上述基礎(chǔ)上提交了filename和descname參數(shù),如何將pdf保存下來盏浙,由于pdf文件比較大磕蒲,有的達到10M,因此這里會用到 Response.iter_content()的使用只盹,有固定的用法辣往,實現(xiàn)的功能是以流的形式保存較大的文件。具體可以參考殖卑。
( http://stackoverflow.com/questions/16694907/how-to-download-large-file-in-python-with-requests-py )
4.要再說一點的就是有時候用requests.get()去請求網(wǎng)址時站削,加上headers雖說是為了偽裝成瀏覽器進行訪問,但有時反而會沒有反應孵稽,去掉headers后反而就直接出了結(jié)果许起,這是自己不解的地方。
最后貼上代碼和抓取結(jié)果(243個pdf文件菩鲜,最大有24M)园细,如下:
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
import requests
from bs4 import BeautifulSoup
from urllib import parse
import re
import time
from multiprocessing import Pool
headers={
'Host': 'www.shclearing.com',
'Connection': 'keep-alive',
'Content-Length': '478',
'Cache-Control': 'max-age=0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Origin': 'http://www.shclearing.com',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Cookie': '_ga=GA1.2.1076225437.1465265795; JSESSIONID=24XcXXCb1bkkKWnKn1LLNBqGTR8qnfmcmf3p5T2pw2TwpNCytnfp!301927905; HDSESSIONID=fhwLXXLPrBqXwsvCyqKL1lCbp5QmGDxxGJ4wd8vrfhr2ksgJcpvf!1660475047; Hm_lvt_d885bd65f967ea9372fc7200bc83fa81=1465223724; Hm_lpvt_d885bd65f967ea9372fc7200bc83fa81=1465354426'
}
url1='http://www.shclearing.com/xxpl/fxpl/index.html'
url2=['http://www.shclearing.com/xxpl/fxpl/index_{}.html'.format(str(i)) for i in range(1,5,1)]
url2.insert(0,url1)
links=[]
host='http://www.shclearing.com/xxpl/fxpl/'
def get_link_list(url):
for item in url:
web_data=requests.get(item)
soup=BeautifulSoup(web_data.text,'lxml')
list=soup.select('ul.list li a')
for item in list:
link=host+item.get('href').split('./')[1]
links.append(link)
FileName=[]
DownName=[]
DownName1=[] #DownName1用于存放轉(zhuǎn)碼后的名稱
def get_contents(link):
web_data=requests.get(link)
soup=BeautifulSoup(web_data.text,'lxml')
contents=soup.select('#content > div.attachments > script')[0].get_text()
a=str(re.findall(r"fileNames = '(.*?)'",contents,re.M)[0])
b=str(re.findall(r"descNames = '(.*?)'",contents,re.M)[0])
FileName=a.replace('./','').split(';;')
DownName=b.split(';;') #先用正則表達式提取出后面post中要用到的兩個參數(shù),但是要將中文轉(zhuǎn)化為對應的URL編碼
for item in DownName:
a=parse.quote(item)
DownName1.append(a)
print(FileName,'\\n',DownName,'\\n',DownName1)
for i,j,k in zip(FileName,DownName1,DownName):
download_file(i,j,k)
# link='http://www.shclearing.com/xxpl/fxpl/cp/201606/t20160608_159680.html'
# get_contents(link)
# print('The pdf have been downloaded successfully !')
def download_file(a,b,c):
data={
'FileName':a,
'DownName':b }
local_filename = c
post_url='http://www.shclearing.com/wcm/shch/pages/client/download/download.jsp'
time.sleep(0.5) #限制下載的頻次速度接校,以免被封
# NOTE the stream=True parameter
r = requests.post(post_url, data=data, headers=headers, stream=True)
with open(local_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024): # 1024 是一個比較隨意的數(shù)猛频,表示分幾個片段傳輸數(shù)據(jù)。
if chunk: # filter out keep-alive new chunks
f.write(chunk)
f.flush() #刷新也很重要蛛勉,實時保證一點點的寫入鹿寻。
return local_filename
if __name__=='__main__':
get_link_list(url2)
pool=Pool()
pool.map(get_contents,links)
print('The documents have been downloaded successfully !')
如果要將下載下來的文檔的保存的路徑存入mysql中,需要再加上下面的語句:
#conn=pymysql.connect(host='localhost',user='root',passwd='root',db='mysql',charset='utf8')
#cursor = conn.cursor()
#cursor.execute('CREATE TABLE root (id int auto_increment primary key,content varchar(100))')
以及在download_file(a,b,c)函數(shù)中的return local_filename語句前加上:
cursor.execute('insert into root(content) values (%s)',local_filename)