爬取中國知網CNKI的遇到的坑與技術總結
參考部落格及資料
- 【python2.7】爬取知網論文
- python實現CNKI知網爬蟲
- 《Python3網路爬蟲開發實戰》崔慶才
最近要寫一個數據分析的專案,需要根據關鍵詞爬取近十年期刊的主要資訊,記錄一下爬取過程中遇到的問題
分析
cnki算是對爬蟲作了一定抵禦,我們要爬取學術論文詳情頁的主題,摘要等資訊,主要步驟和其他網站的爬取大致相似:一是要根據關鍵詞搜尋到列表頁;二是要從列表頁請求得到詳情頁,從詳情頁取得我們所要的資訊。
-
入口頁面:[kns.cnki.net/kns/brief/d…]
-
搜尋後,js動態渲染的請求列表頁面:[kns.cnki.net/kns/brief/b…
-
從列表頁的連結中解析得到詳情頁[kns.cnki.net/KCMS/detail…...]
-
詳情頁,這裡就只要解析網頁即可,我們通過xpath可以很容易得到題目,作者,關鍵詞,摘要等資訊
Scrapy實戰
-
如何設定cookie:
- settings中設定
COOKIES_ENABLED=True
; - http請求參考Scrapy - how to manage cookies/sessions
- 補充:cookiejar模組的主要作用是提供可儲存的cookie物件,可以捕獲cookie並在後續連線請求時重新發送,實現模擬登入功能。在scrapy中可以在請求是傳入meta引數設定,根據不同會話記錄對應的cookie:
- settings中設定
-
如何請求入口頁:(CJFQ代表期刊,可根據需求更改)
data = {
"txt_1_sel": "SU$%=|",
"txt_1_value1": self.key_word,
"txt_1_special1": "%",
"PageName": "ASP.brief_default_result_aspx",
"ConfigFile": "SCDBINDEX.xml",
"dbPrefix": "CJFQ",
"db_opt": "CJFQ",
"singleDB": "CJFQ",
"db_codes": "CJFQ",
"his": 0,
"formDefaultResult": "",
"ua": "1.11",
"__": time.strftime('%a %b %d %Y %H:%M:%S') + ' GMT+0800 (中國標準時間)'
}
query_string = parse.urlencode(data)
yield Request(url=self.home_url+query_string,
headers={"Referer": self.cur_referer},
cookies={CookieJar: 1},
callback=self.parse)
複製程式碼
- 如何請求列表頁
def parse(self, response):
data = {
'pagename': 'ASP.brief_default_result_aspx',
'dbPrefix': 'CJFQ',
'dbCatalog': '中國學術期刊網路出版總庫',
'ConfigFile': 'SCDBINDEX.xml',
'research': 'off',
't': int(time.time()),
'keyValue': self.key_word,
'S': '1',
"recordsperpage": 50,
# 'sorttype': ""
}
query_string = parse.urlencode(data)
url = self.list_url + '?' + query_string
yield Request(url=url,
headers={"Referer": self.cur_referer},
callback=self.parse_list_first)
複製程式碼
- 如何解析列表頁
- 獲得列表總頁數:
response.xpath('//span[@class="countPageMark"]/text()').extract_first() max_page = int(page_link.split("/")[1]) 複製程式碼
- 請求每個列表頁
data = { "curpage": page_num,#迴圈更改 "RecordsPerPage": 50, "QueryID": 0, "ID":"", "turnpage": 1, "tpagemode": "L", "dbPrefix": "CJFQ", "Fields":"", "DisplayMode": "listmode", "PageName": "ASP.brief_default_result_aspx", "isinEn": 1 } 複製程式碼
- 解析列表頁(這裡如果結果為空,請檢查你是否正確設定了cookie)
tr_node = response.xpath("//tr[@bgcolor='#f6f7fb']|//tr[@bgcolor='#ffffff']") for item in tr_node: paper_link = item.xpath("td/a[@class='fz14']/@href").extract_first() 複製程式碼
- 如何解析詳情頁(只是一個示例,有很多種解析方法)
title = response.xpath('//*[@id="mainArea"]/div[@class="wxmain"]/div[@class="wxTitle"]/h2/text()').extract() author = response.xpath('//*[@id="mainArea"]/div[@class="wxmain"]/div[@class="wxTitle"]/div[@class="author"]/span/a/text()').extract() abstract = response.xpath('//*[@id="ChDivSummary"]/text()').extract() keywords = response.xpath('//*[@id="catalog_KEYWORD"]/following-sibling::*/text()').extract() 複製程式碼
歡迎fork我的Github專案
2018.12.15 更新
上面專案在爬取數量不多的專案時不會報錯,但是我嘗試爬取20w資料量的論文時,發現每次只有1000多條資料,觀察發現請求轉到vericode.aspx
頁,即驗證碼頁面。因為我不怎麼會處理驗證碼啦,所以果斷放棄,使用手機知網的介面wap.cnki.net/touch/web,發現真的so easy!
- 請求列表頁
第一步還是要觀察請求,開啟DevTool 可以簡單的構造第一個請求(GET):
def start_requests(self):
data = {
"kw": self.key_word,
"field":5
}
url = self.list_url + '?' + parse.urlencode(data)
yield Request(url=url,
headers=self.header,
meta={'cookiejar': 1},
callback=self.parse)
複製程式碼
-
得到第一頁列表頁,篩選條件,得到請求的FormData
FormData中,我們可以複製下來後,修改的幾個變數來進行篩選:
我們在網頁進行篩選操作,可以看到類似結果:- pageindex:第幾頁列表頁(1 ~ )
- fieldtype: 主題/篇名/全文/作者/關鍵詞/單位/摘要/來源
- sorttype: 相關度/下載次數/被引頻次/最新文獻/歷史文獻
- articletype:文獻型別
- starttime_sc: 開始年份
- endtime_sc: 結束年份
def parse(self, response): self.header['Referer'] = response.request.url yield FormRequest(url=self.list_url, headers = self.header, method = 'POST', meta = {'cookiejar': 1}, formdata = self.myFormData, callback = self.parse_list, dont_filter = True) 複製程式碼
-
解析得到總列表頁數,並構造請求
#總頁數 paper_size = int(response.xpath('//*[@id="totalcount"]/text()').extract_first()) #構造請求 for page in range(1, paper_num): self.myFormData["pageindex"] = str(page), yield FormRequest(url=self.list_url, headers = self.header, method = 'POST', meta = {'cookiejar': page+1, 'page': page},#更新會話 formdata = self.myFormData, callback = self.parse_list_link, dont_filter = True) 複製程式碼
注意:我們觀察請求過程,在網頁中我們是通過點選更新的,我們觀察LoadNextPage函式,可以看到請求更新資料也是通過提交表單的方式,因此我們可以構造POST請求資料。
-
請求詳情頁
items = response.xpath('//a[@class="c-company-top-link"]/@href').extract() #可以將已爬取詳情頁數寫入檔案進行記錄 with open('../record_page.txt', 'a') as f: f.write(str(response.meta['page']) + '\n') for item in items: yield Request(url = item, meta={'cookiejar': response.meta['cookiejar']},#對應會話標誌 headers = self.header, callback = self.parse_item) 複製程式碼
-
解析詳情頁(示例)
baseinfo = response.xpath('/html/body/div[@class="c-card__paper2"]') keywords = baseinfo.xpath('//div[contains(text(),"關鍵詞")]/following-sibling::*/a/text()').extract() 複製程式碼
補充
為了提高爬取速度,防止ip被識別的可能,推薦阿布雲進行IP代理,申請賬號及HTTP動態隧道後,更改settings:
DOWNLOAD_DELAY = 0.2
DOWNLOADER_MIDDLEWARES = {
'myspider.middlewares.RandomUserAgentMiddleware': 401,
'myspider.middlewares.ABProxyMiddleware': 1,
}
AB_PROXY_SERVER = {
'proxyServer': "http://http-dyn.abuyun.com:9020",
'proxyUser': "xxxxxxxxxxxxxxx",#你的
'proxyPass': "xxxxxxxxxxxxxxx"#你的
}
複製程式碼
新增中介軟體:
proxyAuth = "Basic " + base64.urlsafe_b64encode(bytes((proxyUser + ":" + proxyPass), "ascii")).decode("utf8")
class ABProxyMiddleware(object):
""" 阿布雲ip代理配置 """
def process_request(self, request, spider):
request.meta["proxy"] = proxyServer
request.headers["Proxy-Authorization"] = proxyAuth
複製程式碼
爬蟲菜鳥,有問題請幫忙指出!