爬取知網英漢雙語雙語例句

網頁:http://dict.cnki.net/dict_result.aspx

需要用到的庫:

import requests
from lxml import etree
import time
import re
from requests.adapters import HTTPAdapter
  • 第一部首先獲取網頁源代碼。可以看到數據是放在網頁源代碼的,並不是通過放在 json 文件中異步記載的。那麼我們直接找到放有源代碼的報文中構造我們的請求頭:

在這裏插入圖片描述
通過查看 Preview 可以看到網頁源代碼是放在:dict_result.aspx 中,那麼我們直接根據它的 headers 構造請求頭:

在這裏插入圖片描述

def crawl(url):
    header = {
        'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        'accept-encoding': "gzip,deflate",
        'accept-language': "zh-CN,zh;q=0.9,en;q=0.8",
        'cache-control': "no-cache",
        'connection': "keep-alive",
        'cookie': "Ecp_ClientId=1190617212402883927; cnkiUserKey=41b7ffaf-85e4-417d-376e-9d896e89b5fc; OUTFOX_SEARCH_USER_ID_NCOO=202854832.3388026; UM_distinctid=17096417d21285-090cc6aba1c552-6701b35-144000-17096417d223dc; ASP.NET_SessionId=etpdn15sj14efvr2ymak15hg; SID=203006; amid=fc0a8fe8-c8fe-42d5-a63f-9d0e7f494dc8; hw=wordList=%u52a8%u6001%u89c4%u5212%5e%u8ba1%u7b97%u673a%5e%u8ba1%u7b97%5e%u4e8c%u53c9%u6811%5e%u7a7f%u7ebf%u4e8c%u53c9%u6811%5e%u6811%5e%u4e8c%u5206%u7b97%u6cd5; CNZZDATA3209959=cnzz_eid%3D231822529-1583409734-null%26ntime%3D1586737892",
        'host': "dict.cnki.net",
        'upgrade-insecure-requests': "1",
#         'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
        'user-agent': "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36"
    }
    try:
        s = requests.Session()
        s.mount('http://',HTTPAdapter(max_retries=3))#設置重試次數爲3次
        s.mount('https://',HTTPAdapter(max_retries=3))
        response = s.get(headers=header,url=url,timeout=5)
        response.close()
        response.encoding = response.apparent_encoding
    except Exception as e:
        print(e)
        return ""
    return response.text

跟着網頁有什麼填什麼就完事了。

  • 第二步解析網頁源代碼,以關鍵字 動態規劃 爲例子。我們要獲取全部的雙語例句首先要知道有多少頁的數據。

頁面信息所在位置點擊檢查可以看到:
在這裏插入圖片描述
這裏我的做法是將這部分的數據先正則表達式匹配下來,再進一步解析,先看代碼:

# 獲取頁面數,最小爲1
def page_num(html):
    if html==None:
        return 1
    # 獲取有關頁面信息的字符串規則
    pattern1 = re.compile(r'更多雙語句對:(.*?)<\/a>[ ]*<\/tr>',re.I)
    if len(re.findall(pattern1,str(html)))==0:
        return 1
    else: page_info = re.findall(pattern1,str(html))[0]
    
    pattern2 = re.compile(r'<a.*?>(\d)<\/a>',re.I)
    max_page = int(re.findall(pattern2,str(page_info))[-1])
    return max_page

上面爲什麼這麼寫主要要分析不同的情況,當只有一頁數據時:界面是這樣的:
在這裏插入圖片描述
這個時候由於 pattern1 匹配不到所以返回 1.。

由於 pattern2:<a.*?>(\d)<\/a> 只會匹配到

在這裏插入圖片描述
中間是數字的情況,所以若有多頁的話,那麼最後一個數據就是它的總頁數。

在這裏插入圖片描述
下一頁和尾頁由於不是整數所以不會匹配到。

  • 下一步分析雙語例句的結構,解析並提取,這裏解析的方式用 xpath

首先我們先看例句所在的 xpath 表達式是怎麼樣的:

在這裏插入圖片描述
在例句所在位置點擊檢查,再到elements中右鍵點擊複製 xpath 表達式:

//*[@id="showjd_0"]/tbody/tr[1]/td/text()
//*[@id="showjd_1"]/tbody/tr[1]/td/text()

通過對比可以發現每一個短語對應的例句對應不同的 id,並且是逐一增加的。這樣的話,我們在解析頁面時還得先知道這個頁面有多少個短語,所以先解析頁面存在的短語,同理點擊短語查看xpath表達式:

//*[@id="lblresult"]/table[1]/tbody/tr[2]/td/table/tbody/tr[1]/td/table[1]/tbody/tr/td/font/b/a
//*[@id="lblresult"]/table[1]/tbody/tr[2]/td/table/tbody/tr[1]/td/table[3]/tbody/tr/td/font/b/a

可以看到每一個短語之間的間隔是 table+2。
這樣我們可以構造我們的xpath表達式爲 ://*[@id="lblresult"]/table[1]/tr[2]/td/table/tr[1]/td/table['+str(i)+']/tr/td/font/b/a

# //*[@id="lblresult"]/table[1]/tr[2]/td/table/tbody/tr[1]/td/table[15]/tr/td/font/b/a
# 獲取頁面中存在的短語,返回一個短語列表
def parse_phrase(html):
    if html == None:
        return []
    try:
        phrase_list = []
        tree = etree.HTML(html)
        # 先計算table節點有多少個,才能知道循環的上限
        num = int(tree.xpath('count(//*[@id="lblresult"]/table[1]/tr[2]/td/table/tr[1]/td/table)'))
        for i in range(1,num,2):
            phrase_xpath = '//*[@id="lblresult"]/table[1]/tr[2]/td/table/tr[1]/td/table['+str(i)+']/tr/td/font/b/a'
            phrase = tree.xpath(phrase_xpath+'//text()')[0].strip()
            phrase_list.append(phrase)
    except Exception as e:
        print(e)
    return phrase_list

有了短語列表了以後,我們就可以知道例句中的 id 的範圍。

# //*[@id="showjd_0"]/tbody/tr[1]/td
# start 是 id 的起點
# end 是 id 的終點
def parse_original_sentence(html,start,end):
    if html == None:
        return "",""
    tree = etree.HTML(html)
    e_string = ""
    c_string = ""
    for i in range(start,end+1):
        id = '//*[@id="showjd_' + str(i) + "\"]"
        num = int(tree.xpath('count('+id+'/tr)'))
        for index in range(1,num,3):
            e_sentence_xpath = id + "/tr[" + str(index) + "]/td//text()"
            en_list = tree.xpath(e_sentence_xpath)
            en_list = [ii.strip() for ii in en_list]
            en = " ".join(en_list)
            e_string = e_string + en.lower().strip()
            e_string = e_string + '\n'
#             print(en)
            
            c_sentence_xpath = id + "/tr[" + str(index+1) + "]/td//text()"
            ch_list = tree.xpath(c_sentence_xpath)
            ch_list = [ii.strip() for ii in ch_list]
            ch = "".join(ch_list)
            c_string = c_string + ch.strip()
            c_string = c_string + '\n'
#             print(ch)
    return e_string,c_string

同樣的分析方法用來爬取 更多界面的中的未完全匹配句對
在這裏插入圖片描述

# http://dict.cnki.net/dict_more_sen.aspx?searchword=%E7%A9%BA%E9%97%B4&unvsm=1&t=&s=0&c=506&z=I138&page=2
# //*[@id="lblresult"]/table/tbody/tr/td/table/tbody/tr[3]/td/table/tbody/tr[2]/td
def crawl_and_parse_more_html(data,style):
    prefix_url = "http://dict.cnki.net/dict_more_sen.aspx?searchword=" + data +"&unvsm=1&t=&s=0&c=100&z=" + style +"&page="
    en_string = ""
    ch_string = ""
    for i in range(1,6):
        try:
            url = prefix_url + str(i)
            html = crawl(url)
            time.sleep(0.1)
            tree = etree.HTML(html)
            num = int(tree.xpath('count(//*[@id="lblresult"]/table/tr/td/table/tr[3]/td/table/tr)'))
            if num == 4:
                break
            else:
                for index in range(2,num,3):
                    en_xpath = '//*[@id="lblresult"]/table/tr/td/table/tr[3]/td/table/tr[' + str(index) + ']/td//text()'
                    en_list = tree.xpath(en_xpath)
                    en_list = [ii.strip() for ii in en_list]
                    en = " ".join(en_list)
                    en_string = en_string + en.lower().strip()
                    en_string = en_string + '\n'
                    
                    ch_xpath = '//*[@id="lblresult"]/table/tr/td/table/tr[3]/td/table/tr[' + str(index+1) + ']/td//text()'
                    ch_list = tree.xpath(ch_xpath)
                    ch_list = [ii.strip() for ii in ch_list]
                    ch = "".join(ch_list)
                    ch_string = ch_string + ch.strip()
                    ch_string = ch_string + '\n'
#                     print(ch)
        except Exception as e:
            print(e)
            
    return en_string,ch_string

下面來解析一下網頁連接:
在這裏插入圖片描述
可以看到 style 指的是 查詢分類,page 指的是當前所在的頁面,searchword 是檢索的關鍵詞。

其中分類中 I138 爲 計算機軟件及計算機應用,A002 爲數學,I139 爲互聯網技術。知道這些就可以構造 url 爬取網頁了。

主函數:

# http://dict.cnki.net/dict_result.aspx?searchword=%e7%ae%97%e6%b3%95&tjType=sentence&style=I138&page=2
if __name__ == "__main__":
    # 用來獲取檢索關鍵字的文本
    data_file = r"E:/大四下/數據/例句/知網例句/關鍵詞2.txt"
    
    # 英語例句存放的文件
    en_save_file = r"E:/大四下/數據/例句/知網例句/cnki_liju.en"
    
    # 中文例句存放的文件
    ch_save_file = r"E:/大四下/數據/例句/知網例句/cnki_liju.ch"
    
    # 這個是短語存放的文件
#     phrase_save_file = r'E:/大四下/數據/例句/知網例句/關鍵詞2.txt'
    
    # 以追加的方式打開
    en_fw = open(en_save_file,'a+',encoding='utf8')
    ch_fw = open(ch_save_file,'a+',encoding='utf8')
#     phrase_fw = open(phrase_save_file,'a+',encoding='utf8')
    
    # 定義查詢類別列表
    style = ['A002','I139','I138']
    
    with open(data_file,'r',encoding='utf8') as fr:
        # 獲取檢索關鍵詞
        line = fr.readline()
        while line:
            if line!='\n':
                data = line.strip()
                
                # 構造公共前綴 url
                prefix_url = "http://dict.cnki.net/dict_result.aspx?searchword=" + data 
                
                # 迭代類別分別爬取
                for s in style:
                    print("data;{},s:{}".format(data,s))
                    prefix_url_tmp = prefix_url + "&tjType=sentence&style="+ s + "&page="
                    
                    # 獲取頁數
                    url_for_get_page_size = "http://dict.cnki.net/dict_result.aspx?searchword=" + data + "&style="+s+"&tjType=sentence"
                    html_for_get_page_size = crawl(url_for_get_page_size)
                    if html_for_get_page_size == "":
                        continue
                    time.sleep(0.1)
                    page = page_num(html_for_get_page_size)
#                     print(page)

                    # 記錄例句 id 的起始
                    start = 0
                    end = 0
                    for i in range(1,page+1):
                        url = prefix_url_tmp + str(i)
#                         print(url)
                        original_html = crawl(url)
                        if original_html == "":
                            continue
                        time.sleep(0.1)
                    
                        #獲取短語列表並寫入文件
                        phrase_list = parse_phrase(original_html)
#                         for phrase in phrase_list:
#                             if phrase[0].encode('UTF-8').isalpha():
#                                 print(phrase)
#                                 phrase_fw.write(phrase)
#                                 phrase_fw.write('\n')
#                         print(phrase_list)
                        
                        # 根據短語列表計算 id 的起始
                        end = end + len(phrase_list)
                        print("start:{} and end:{}".format(start,end))
                        e_string,c_string = parse_original_sentence(original_html,start,end)
                        start = end
                        en_fw.write(e_string)
                        ch_fw.write(c_string)
                    
                    # 爬取更多頁面中的例句
                    e_string,c_string = crawl_and_parse_more_html(data,s)
                    en_fw.write(e_string)
                    ch_fw.write(c_string)
                    en_fw.flush()
                    ch_fw.flush()

            line = fr.readline()
            
    en_fw.close()
    ch_fw.close()
    phrase_fw.close()
    print('ok')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章