爬取项目数据集

2021SC@SDUSC

简介

根据项目进度安排,需要爬取百度学术生成数据集来测试不同模型的性能表现。然而在实际爬取时遇到了例如页面重复、页面无法访问等问题。

页面重复问题描述及解决方案

在爬取结束后发现出现了大量的重复页面,一方面浪费了大量时间,另一方面去重需要花费额外的经历。起初,我尝试将页面的第一篇论文题目作为该页面的唯一标识,通过集合来去重,然而这种方式依然无法避免时间浪费的问题。

通过对于算法和百度文库的进一步分析,问题源头逐渐浮出水面。在最初的爬虫程序中有一个page_num参数,表示期望爬取的页面数量。通过对url中“pn=?”的替换实现了对所有检索的遍历。

next_page = fir_page.replace("pn=10", "pn={:d}".format(i * 10))

然而部分关键词的检索结果达不到page_num的数值,例如搜索“检索”只有68页

在这里插入图片描述

当pn=?的数值超过临界值时,百度学术的处理方式是回退到第一页,然后继续爬取,正因上述机制,出现了页面重复的问题。

为了解决该问题,我对页面进行了进一步分析。当页面存在下一页是会由一个右箭头,如图:

在这里插入图片描述

可以对页面进行分析来确定是否存在右箭头,如果存在右箭头,那么可以继续遍历,逻辑如下:

next_icon_soup = soup_new.find(id='page')
            if next_icon_soup == None:
                # 为none时整个页面都为none
                print('None_for_next')
                continue
            next_icon_soup2 = next_icon_soup.find_all('a')[-1].find(class_='c-icon-pager-next')
            if next_icon_soup2 == None:
                break

然而在解决该问题时我发现在访问某些页面时返回结果是乱码,此时next_icon_soup2一定为None,即便存在下一页也被迫退出。因此我首先获取next_icon_soup2的父元素,如果父元素不存在,那么一定说明返回结果是乱码,将该页忽略。

页面无法访问问题描述及解决方案

正如前文所提到的,在访问某些页面时返回结果可能为乱码,这对去重也造成了一定的困难,只是单纯的忽略会造成大量数据丢失。引起该问题的根源可能是网站的反爬虫机制,因此最初的想法是每次爬取后睡眠1s来避免被发现,然而效果并不理想。

进一步分析,我尝试对无法访问的页面更换爬虫的访问头,更换后正常访问。基于此,我采取了如下机制:首先使用请求头A进行爬取,如果返回为乱码,更换请求头B,如果仍为乱码再次更换请求头A,因此类推,当更换超过10次仍不能访问后,放弃该页面。采取此机制大多数页面均可正常爬取。

接下来的问题是如何判断爬取的结果为乱码。经过分析,我发现乱码的页面有一个元素类名为“timeout-img”,当页面中出现该元素时,将该页面判定为无法访问的页面。

time_out = soup_new.find(class_='timeout-img')
            if time_out_num > 10:
                fail_num += 1
                flag = False
                print('fail...')
                break

该部分整体实现如下:

        while time_out != None:
            time_out_num += 1
            time.sleep(1)
            print(next_page)
            print(' timeout...')
            response = requests.get(next_page,headers = headers2)
            soup_new = BeautifulSoup(response.text, "lxml")
            # 检查是否超时
            time_out = soup_new.find(class_='timeout-img')
            if time_out_num > 10:
                fail_num += 1
                flag = False
                print('fail...')
                break

小结

解决了上述问题后,爬虫正常运行并构建了符合要求的数据集。

运行结果如下(关键词:牛肉):

在这里插入图片描述

部分数据集截图如下:
在这里插入图片描述

完整代码:

from bs4 import BeautifulSoup
from selenium import webdriver
import time
import pandas as pd
import requests
import re
from collections import defaultdict

headers = {
    
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.50'}
headers2 = {
    
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0'}
def driver_open(key_word):
    url = "http://xueshu.baidu.com/"
#     driver = webdriver.PhantomJS("D:/phantomjs-2.1.1-windows/bin/phantomjs.exe")
    driver = webdriver.Chrome()
    driver.get(url)
    time.sleep(2)
    driver.find_element_by_class_name('s_ipt').send_keys(key_word)
    time.sleep(2)
    driver.find_element_by_class_name('s_btn_wr').click()
    time.sleep(2)
    content = driver.page_source.encode('utf-8')

    soup = BeautifulSoup(content, 'lxml')
    return soup

def page_url_list(soup, page=0):
    global soup_new
    fir_page = "http://xueshu.baidu.com" + soup.find(id='page').find('a')["href"]
    urls_list = []
    num = 0
    fail_num = 0
    for i in range(page):
        num+=1
        print(i)
        # time.sleep(1)
        next_page = fir_page.replace("pn=10", "pn={:d}".format(i * 10))
        response = requests.get(next_page,headers = headers)
        soup_new = BeautifulSoup(response.text, "lxml")
        time_out = soup_new.find(class_='timeout-img')
        time_out_num = 0
        flag = True
        while time_out != None:
            time_out_num += 1
            time.sleep(1)
            print(next_page)
            print(' timeout...')
            response = requests.get(next_page,headers = headers2)
            soup_new = BeautifulSoup(response.text, "lxml")
            # 检查是否超时
            time_out = soup_new.find(class_='timeout-img')
            if time_out_num > 10:
                fail_num += 1
                flag = False
                print('fail...')
                break
        if flag:
            c_fonts = soup_new.find_all("h3", class_="t c_font")
            for c_font in c_fonts:
                url = c_font.find("a")["href"]
                urls_list.append(url)
            # 如果没有新的页面,停止检索
            next_icon_soup = soup_new.find(id='page')
            if next_icon_soup == None:
                # 为none时整个页面都为none
                print('None_for_next')
                continue
            next_icon_soup2 = next_icon_soup.find_all('a')[-1].find(class_='c-icon-pager-next')
            if next_icon_soup2 == None:
                break
    urls_list = set(urls_list)
    print("链接总数量")
    print(len(urls_list))
    print('失败链接数量:',fail_num*10)
    return urls_list



def get_item_info(url):
    content_details = requests.get(url,headers = headers)
    soup = BeautifulSoup(content_details.text, "lxml")
    # 提取文章题目
    try:
        title = ''.join(list(soup.select('#dtl_l > div > h3 > a')[0].stripped_strings))
    except(IndexError):
        title = ''


    # 提取摘要
    try:
        abstract = list(soup.select('div.abstract_wr p.abstract')[0].stripped_strings)[0].replace("\u3000", ' ')
    except(IndexError):
        abstract = ''


    # 提取关键词
    try:
        key_words = ';'.join(key_word for key_word in list(soup.select('div.dtl_search_word > div')[0].stripped_strings)[1:-1:2])
    except(IndexError):
        key_words = ''
    return title,  abstract,  key_words

def get_all_data(urls_list):
    dit = defaultdict(list)
    num = 1
    len_list = len(urls_list)
    for url in urls_list:
        num+=1
        print('{}/{}'.format(num,len_list))
        title,abstract, key_words = get_item_info(url)
        if (len(str(title)) > 0 and len(str(abstract)) > 0 and len(str(key_words)) > 0):
            dit["title"].append(title)
            dit["abstract"].append(abstract)
            dit["key_words"].append(key_words)
    return dit

def save_csv(dit,num):
    data = pd.DataFrame(dit)
    print(data)
    columns = ["title",  "abstract", "key_words"]
    if num == 1:
        data.to_csv("data.csv", mode='a',index=False, columns=columns)
    else:
        data.to_csv("data.csv", mode='a', index=False , header=False)
    print("That's OK!")



if __name__ == "__main__":
    key_words=['牛肉']
    num=1
    for key_word in key_words:
        soup = driver_open(key_word)
        urls_list = page_url_list(soup, page=100)
        dit = get_all_data(urls_list)
        save_csv(dit,num)
        num+=1


Guess you like

Origin blog.csdn.net/Simonsdu/article/details/121086552