1. 程式人生 > >python爬蟲——爬取知網體育學刊引證論文資訊

python爬蟲——爬取知網體育學刊引證論文資訊

前言

國慶百無聊賴,然後幫一個小姐姐爬取知網資訊,覺得知網算目前處理過的對爬蟲稍微有點防範的網站,遂有了這篇部落格

目標

爬取知網上2003年體育學刊文獻所有論文的引證論文,包括論文名稱、作者、發表時間,也就是下面紅框所指處

點選click處,點選黑框,紅框所指處即為要爬取資料:

分析

  1. 知網主體頁面使用ASP(不知道啥東西,類似於jsp一樣的模板吧),我需要爬取的頁面後臺是通過模板解析後返回html頁面,所以不可能捕獲介面獲得json
  2. 知網會有iframe,不過沒有巢狀,iframe可以在html中巢狀html,問過前端同學後,才知道這是兩個html檔案,瀏覽器會把兩個html巢狀顯示,iframe在一定程度上會增加爬取難度,一般iframe的src屬性會標記html檔案的url,但是知網的不是,可能是js處理過的,抓包顯示url不是src屬性的值
  3. 知網會用cookie跟蹤使用者行為
  4. 第一幅圖中,click處的url貌似是經過js處理,直接請求,會出現重定向,返回到知網首頁

難點

  1. 知網採用cookie跟蹤使用者行為以及部分url經過js處理,所以我決定使用selenium+chromedriver,但是太慢了,而且有點佔資源,這裡知網有個小漏洞,引證論文頁面不需要cookie,所以第一遍使用selenium+chromedriver處理獲得所有引證論文url(一共299個),第二遍使用urllib+etree單獨處理引證論文(一共6800+)
  2. 引證論文存在翻頁,但是翻頁後大量資料會有重複,並且頁面形式不固定,例如某些頁面有A、B、C三處有引證論文,有些頁面有A、B、C、D、E五處引證論文,具體參見知網頁面,這裡考慮到程式健壯性,我選擇每次插入資料前先查一遍資料是否存在(有點蠢),雖然可以使用資料庫的unique欄位,但是異常丟擲太頻繁

程式碼

獲得引證論文url

from selenium import webdriver
import time
from lxml import etree
from urllib import request
from dbmanager_zhiwang import dbmanager_paper

def get_paper(page,index):
    html_parse=etree.HTML(page)
    ul=html_parse.xpath('//div[@class="essayBox"]/ul')[index]
    li_list=ul.findall('li')
    for li in li_list:
        a_list=li.findall('a')
        for index in range(0,len(a_list)):
            print(a_list[index].text)
            if index==0:
                print(a_list[index].tail)   

def get_cookie():
    driver.get('http://kns.cnki.net/kns/brief/default_result.aspx')
    time.sleep(5)
    driver.find_element_by_name('txt_1_value1').send_keys('體育學刊')     
    driver.find_element_by_xpath('//select[@id="txt_1_sel"]//option[@value="LY$%=|"]').click()      
    driver.find_element_by_id('btnSearch').click()    
    time.sleep(5)
      
    

#缺少paperid
def get_url(num):
    elements=driver.find_elements_by_xpath('//table[@class="GridTableContent"]//tr[@bgcolor]')
    for element in elements:
        try:
            a=element.find_element_by_xpath('td/a[@class="fz14"]')
            print(a.get_attribute('href'))
            paper_info=element.text.replace('\n',' ').split(' ')
            paper_title=paper_info[1]
            index=2
            author=''
            while('體育學刊' not in paper_info[index]):
                author=author+paper_info[index]
                index=index+1
            date=paper_info[index+1]
            reference=paper_info[index+3]
            insert_info=(str(num),paper_title,author,date,reference,a.get_attribute('href'))      
            a.click()
            windows = driver.window_handles
            driver.switch_to.window(windows[-1])
            time.sleep(5)
            i=0
            while(i<5):
                i=i+1
                if(etree.HTML(driver.page_source).xpath('//div[@class="yzwx"]/a')!=[]):
                    break
            #不存在引證論文
            url0_list=[]
            url1_list=[]
            url2_list=[]
            if(i!=5):
                html_parse=etree.HTML(driver.page_source)
                url=driver.find_element_by_xpath('//div[@class="yzwx"]/a').get_attribute('href')
                if(url!=None):          
                    print(url)             
                    driver.get(url)
                    html_parse=etree.HTML(driver.page_source)
                    a0_list=html_parse.xpath('//span[@id="CJFQ"]//a')
                    a1_list=html_parse.xpath('//span[@id="CDFD"]//a')
                    a2_list=html_parse.xpath('//span[@id="CMFD"]//a')
                    for a in a0_list:     
                        url0_list.append(a.attrib['href'])
                    
                    for a in a1_list:
                        url1_list.append(a.attrib['href'])
                    
                    for a in a2_list:
                        url2_list.append(a.attrib['href'])     
            db.insert_info(insert_info,url0_list,url1_list,url2_list) 
            num=num+1            

        except Exception as arg:
            print (arg)
        driver.close()
        driver.switch_to_window(windows[0])
        time.sleep(5)
    return num


if __name__=="__main__":
    options = webdriver.ChromeOptions()
    prefs = {
        'profile.default_content_setting_values' :
            {
            'notifications' : 2
             }
    }
    options.add_experimental_option('prefs',prefs)
    driver = webdriver.Chrome(chrome_options = options)
    driver.maximize_window()
    get_cookie()
    db=dbmanager_paper('root','12345','127.0.0.1','zhiwang')
    num=0
    now_page=1
    driver.get('http://kns.cnki.net/kns/brief/brief.aspx?ctl=4a7fde68-1a44-4852-8b23-1a70aeb4cf8b&dest=%E5%88%86%E7%BB%84%EF%BC%9A%E5%8F%91%E8%A1%A8%E5%B9%B4%E5%BA%A6%20%E6%98%AF%202003&action=5&dbPrefix=SCDB&PageName=ASP.brief_default_result_aspx&Param=%e5%b9%b4+%3d+%272003%27&SortType=(FFD%2c%27RANK%27)+desc&ShowHistory=1&isinEn=1')

    while(now_page<16):
        num=get_url(num)
        a_list=driver.find_elements_by_xpath('//div[@class="TitleLeftCell"]//a')
        for a in a_list:
            if(a.text=='下一頁'):
                a.click()
                break
        now_page=now_page+1
        time.sleep(5)

獲得引證論文

from selenium import webdriver
import time
from lxml import etree
from urllib import request
import re
from dbmanager_zhiwang import dbmanager_paper
    

def get_paper(url,paperid):
    req=request.Request(url,headers=header)
    html_page=request.urlopen(req).read().lower().decode('utf-8',errors='ignore')
    html_parse=etree.HTML(html_page)
    ul_list=html_parse.xpath('//div[@class="essaybox"]//ul')
    for ul in ul_list:
        li_list=ul.findall('li')
        for li in li_list:
            try:
                a_list=li.itertext()
                info_temp=''
                for a in a_list:
                    info_temp=info_temp+a.replace(' ','').replace('\r\n','').replace('&nbsp&nbsp','')
                info=info_temp.split('.')
                if(db.judge_exist(info[0])==False):
                    length=len(info)-1
                    if(length<3):
                        deal=info[length]
                        date=re.findall('\d.*',deal)[0]
                        workunit=deal.replace(date,'').replace('年','')
                        info[length]=workunit
                        info.append(date)
                    info.append(str(paperid))
                    if(db.insert_paper_info(tuple(info))==False):
                        return False
            except Exception as arg:
                print(arg)
                return False
    return True


if __name__=="__main__":
    header={
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
        }
    db=dbmanager_paper('root','Ll41655184165518','127.0.0.1','zhiwang')
    while(True):
        status='finish'
        result=db.get_url()
        if(result==[]):
            break
        print(result[2])
        if(get_paper(result[2],result[1])==False):
            status='error'
        status_info=(status,result[0])
        db.set_status(status_info)
        time.sleep(10)
        

最後把資料庫中的資料轉換為excel,部落格中沒有給出資料庫結構,詳情見gayhub(沒有打錯):https://github.com/zhuoyunli/crawler_zhiwang,裡面有資料庫檔案以及處理好的excel檔案