天天看點

Python3爬取知網文章

分析

首先,我們看一下入口的網站,在輸入關鍵詞搜尋之前和之後它的網址并沒有什麼變化,是以我們不能通過直接請求它來得到文章。

搜尋前

Python3爬取知網文章

搜尋後

Python3爬取知網文章

是以,我們應該換一種思路。打開開發者工具後,我們可以看到如下的内容

Python3爬取知網文章

通過對比,我們可以基本上确定這個網址就是我們要爬取的網站了。它的url是

https://kns.cnki.net/kns/brief/brief.aspx?pagename=ASP.brief_default_result_aspx&isinEn=1&dbPrefix=SCDB&dbCatalog=%e4%b8%ad%e5%9b%bd%e5%ad%a6%e6%9c%af%e6%96%87%e7%8c%ae%e7%bd%91%e7%bb%9c%e5%87%ba%e7%89%88%e6%80%bb%e5%ba%93&ConfigFile=SCDBINDEX.xml&research=off&t=1572329280069&keyValue=%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6&S=1&sorttype=

參數如下
           
Python3爬取知網文章

那我們是不是通過構造這些參數就可以通路了我們要的文章呢?這并不一定,點選剛才的連結,我們可能會得到這樣的資訊

Python3爬取知網文章

而要想通過通路連結就直接得到内容的話,你就需要像正常通路知網一樣,輸入關鍵詞進行搜尋。這時,知網的伺服器才會認為伺服器上存在了使用者,才會給你資料。那這樣的話,我們就應該找POST方法,并向它傳遞包括關鍵詞在内的一系列參數就可以了。同樣在開發者工具下,我們找到了這樣的内容

Python3爬取知網文章

通過名字以及傳輸的字段我們可以很清楚地知道這個就是我們要進行post的網址了。

代碼實作

1. 發送post請求

知網的formdata
           
zhiwangFormdata={
    'action':'',
    'ua': '1.11',
    'isinEn': '1',
    'PageName': 'ASP.brief_default_result_aspx',
    'DbPrefix': 'SCDB',
    'DbCatalog': '中國學術文獻網絡出版總庫',
    'ConfigFile': 'SCDBINDEX.xml',
    'db_opt': 'CJFQ,CDFD,CMFD,CPFD,IPFD,CCND,CCJD',
    'txt_1_sel': 'SU$%=|',
    'txt_1_value1': '',
    'txt_1_special1':'%',
    'his': '0'
}
           
發送POST
           
session=requests.session()
post_url='https://kns.cnki.net/kns/request/SearchHandler.ashx'
zhiwangFormdata['txt_1_value1']=keyWord
response=session.post(post_url,data=zhiwangFormdata)
           

2. 通過Requests獲得首頁文章資訊

注:這裡的pq用的是pyquery
           
url='https://kns.cnki.net/kns/brief/brief.aspx?pagename='+response.text+'&keyValue='+quote(keyWord)+'&S=1&sorttype='
response=session.get(url)
html=pq(response.text)
totalNumber=html.find('div.pageBar_min>div.pagerTitleCell').text().replace('找到','').replace('條結果','').strip()
#沒有論文
if totalNumber=='0':
	print('沒有找到')
    return
print('正在爬取第1頁')
get_detail(html)
           
獲得文章詳細資訊
           
def get_detail(html):
    allItems=html.find('table.GridTableContent>tr').items()
    count=0
    for item in allItems:
        if count==0:
            count=1
            continue
        n_l=item('td:nth-child(2)>a')
        #擷取文章名字及連結
        dict={
            'name':n_l.text().replace('\n',''),
            'link':'https://kns.cnki.net'+n_l.attr('href').replace('/kns','/KCMS')
        }
        print(dict)
           

3.構造剩餘頁面url并爬取

在爬取到第一頁後,得到的源代碼中還有下一頁的連結,通過提取以及構造,我們就可以一直爬取下去
           
#找出總的頁數
    totalPage = html.find('span.countPageMark').text().split('/')[1]
    totalPage=int(totalPage)
    #現在從第二頁開始爬,一直爬到totalPage就行
    count=2
    #獲得通用的link
   nextUrl='https://kns.cnki.net/kns/brief/brief.aspx'+html.find('div.TitleLeftCell>a').attr('href')
    while count<=totalPage:
        print('正在爬取第'+str(count)+'頁')
        #構造URL
        trueUrl=nextUrl.replace('curpage=2','curpage='+str(count))
        response=session.get(trueUrl)
        #出現驗證碼,對驗證碼進行處理
        if response.url!=trueUrl:
            yanzhenma(session,response.url)
            continue
        html=pq(response.text)
        get_detail(html)
        count += 1

           

4. 驗證碼處理

在爬取知網的過程中,我們還要對付驗證碼。這個地方我選擇的是把驗證碼圖檔下載下傳下來,在本地進行識别(我使用的是百度的智能雲),在之後在向伺服器發送一個帶有識别結果的POST請求就行。
擷取驗證碼圖檔,我們通過構造一個含有一個随機數的網址,發送請求,獲得圖檔
而知網的POST驗證碼POST網頁構造比較簡單,直接将驗證碼網址加上你識别出來的結果就是你要請求網址了
           
#驗證碼處理函數
def yanzhenma(session,url):
    print('識别驗證碼中......')
    session.get(url)
    response = session.get('https://kns.cnki.net/kns/checkcode.aspx?t=' + quote("'" + str(random())))
    image = open('image.jpg', 'wb')
    image.write(response.content)
    image.close()
    result =verify('image.jpg').lower()
    #或者檢視image.jpg手動輸入
    #result=input().lower()
    print('驗證碼為'+result)
    requestUrl=url+'&vericode='+quote(result)
    session.get(requestUrl)
           
驗證碼識别函數
           
def convertimg(path):
    img = Image.open(path)
    width, height = img.size
    while(width*height > 4000000):  # 該數值壓縮後的圖檔大約 兩百多k
        width = width // 2
        height = height // 2
    new_img=img.resize((width, height),Image.BILINEAR)
    format=path.split('.')[1]
    new_img.convert('RGB').save('temp.'+format)
def baiduOCR(path):
    APP_ID = '你的APP_ID'
    API_KEY = '你的API_KEY'
    SECRECT_KEY = '你的SECRECT_KEY'
    client = AipOcr(APP_ID, API_KEY, SECRECT_KEY)
    format = path.split('.')[1]
    i = open('temp.'+format, 'rb')
    img = i.read()
    message = client.basicGeneral(img)   # 通用文字識别,每天 50 000 次免費
    #message = client.basicAccurate(img)   # 通用文字高精度識别,每天 800 次免費
    i.close()
    if len(message.get('words_result'))==0:
        return ''
    return message.get('words_result')[0]["words"]
def verify(path):
    convertimg(path)
    result=baiduOCR(path)
    return result
           

最後,附上完整代碼

#知網
from pyquery import PyQuery as pq
from urllib.parse import quote
from random import random
from aip import AipOcr
from PIL import Image
import requests
import settings
zhiwangFormdata={
    'action':'',
    'ua': '1.11',
    'isinEn': '1',
    'PageName': 'ASP.brief_default_result_aspx',
    'DbPrefix': 'SCDB',
    'DbCatalog': '中國學術文獻網絡出版總庫',
    'ConfigFile': 'SCDBINDEX.xml',
    'db_opt': 'CJFQ,CDFD,CMFD,CPFD,IPFD,CCND,CCJD',
    'txt_1_sel': 'SU$%=|',
    'txt_1_value1': '',
    'txt_1_special1':'%',
    'his': '0'
}
def convertimg(path):
    img = Image.open(path)
    width, height = img.size
    while(width*height > 4000000):  # 該數值壓縮後的圖檔大約 兩百多k
        width = width // 2
        height = height // 2
    new_img=img.resize((width, height),Image.BILINEAR)
    format=path.split('.')[1]
    new_img.convert('RGB').save('temp.'+format)
def baiduOCR(path):
    APP_ID = '你的APP_ID'
    API_KEY = '你的API_KEY'
    SECRECT_KEY = '你的SECRECT_KEY'
    client = AipOcr(APP_ID, API_KEY, SECRECT_KEY)
    format = path.split('.')[1]
    i = open('temp.'+format, 'rb')
    img = i.read()
    message = client.basicGeneral(img)   # 通用文字識别,每天 50 000 次免費
    #message = client.basicAccurate(img)   # 通用文字高精度識别,每天 800 次免費
    i.close()
    if len(message.get('words_result'))==0:
        return ''
    return message.get('words_result')[0]["words"]
def verify(path):
    convertimg(path)
    result=baiduOCR(path)
    return result
def get_all(keyWord):
    session=requests.session()
    post_url='https://kns.cnki.net/kns/request/SearchHandler.ashx'
    settings.zhiwangFormdata['txt_1_value1']=keyWord
    response=session.post(post_url,data=zhiwangFormdata)
    url='https://kns.cnki.net/kns/brief/brief.aspx?pagename='+response.text+'&keyValue='+quote(keyWord)+'&S=1&sorttype='
    response=session.get(url)
    html=pq(response.text)
    totalNumber=html.find('div.pageBar_min>div.pagerTitleCell').text().replace('找到','').replace('條結果','').strip()
    #沒有論文
    if totalNumber=='0':
        print('沒有找到')
        return
    print('正在爬取第1頁')
    get_detail(html)
    totalPage = html.find('span.countPageMark').text().split('/')[1]
    totalPage=int(totalPage)
    #現在從第二頁開始爬,一直爬到totalPage就行
    count=2
    #獲得通用的link
    nextUrl='https://kns.cnki.net/kns/brief/brief.aspx'+html.find('div.TitleLeftCell>a').attr('href')
    while count<=totalPage:
        print('正在爬取第'+str(count)+'頁')
        #構造URL
        trueUrl=nextUrl.replace('curpage=2','curpage='+str(count))
        response=session.get(trueUrl)
        if response.url!=trueUrl:
            yanzhenma(session,response.url)
            continue
        html=pq(response.text)
        get_detail(html)
        count += 1
def yanzhenma(session,url):
    print('識别驗證碼中......')
    session.get(url)
    response = session.get('https://kns.cnki.net/kns/checkcode.aspx?t=' + quote("'" + str(random())))
    image = open('image.jpg', 'wb')
    image.write(response.content)
    image.close()
    #result =yanzhen.verify('image.jpg').lower()
    # 或者檢視image.jpg手動輸入
    result=input().lower()
    print('驗證碼為'+result)
    requestUrl=url+'&vericode='+quote(result)
    session.get(requestUrl)
def get_detail(html):
    allItems=html.find('table.GridTableContent>tr').items()
    count=0
    for item in allItems:
        if count==0:
            count=1
            continue
        n_l=item('td:nth-child(2)>a')
        dict={
            'name':n_l.text().replace('\n',''),
            'link':'https://kns.cnki.net'+n_l.attr('href').replace('/kns','/KCMS')
        }
        print(dict)
def run():
    keyWord=input('請輸入搜尋詞')
    get_all(keyWord)
run()

           

測試結果

Python3爬取知網文章