面向特定问题的开源算法管理和推荐(三) | 2021SC@SDUSC

2021SC@SDUSC

面向特定问题的开源算法管理和推荐

一、爬取百度文库数据的思路

首先,我们需要模拟浏览器访问百度学术,然后搜索相关关键词,将得到的页面传回给bs4进行解析。

随后,我们将获得搜索结果的每一个页面,将页面中的学术文档链接提取出来,然后进行逐一访问

我们需要在每一个访问到的学术文档页面中提取关键词、摘要、标题。最后将它们统一保存在数据库或者硬盘上。

二、源码分析与改进

我们使用了学长提供的python爬虫对百度文库的数据进行了爬取

import pymysql
from bs4 import BeautifulSoup
from selenium import webdriver
import time
import pandas as pd
import requests
from selenium.webdriver.chrome.service import Service

主要使用了上面的一些库:比如beautifulsoup4(主要用于lxml解析),selenium(主要用于模拟人类使用浏览器获取信息), pandas,requests。

我们在运行该文件之前,需要在虚拟环境中先将库的内容安装好,并且要下载自己浏览器对应的driver。在这里我使用的是Chrome,因此我先要下载对应的Chromedriver版本。Chromedriver是由Chromium team开发维护的,他通过Chrome的自动代理框架,达到控制浏览器的作用。

其中最后一行的service是后来基于学长的代码和我自己的python版本情况添加修改的。使用service可以控制Chrome driver的生死,做到用完就关闭。

main函数入口:

if __name__ == "__main__":
    key_word = "AI"
    soup = driver_open(key_word)
    urls_list = page_url_list_2(soup, maxpage=72)
    # get_all_data(urls_list)
    save_csv(urls_list = urls_list)

爬虫文件名称为"pabaiduxueshu.py"其内容专为爬取百度文库内容设计。首先key_words设置为爬取内容的关键词,用这个关键词进行搜索。

最后两行有两种保存方式,一种是保存在数据库中,还有一种是保存成csv格式。我选择了后一种。具体的保存方式我将在之后分析。

def driver_open(key_word):
    url = "http://xueshu.baidu.com/"
    service = Service("/你的地址/chromedriver")
    driver = webdriver.Chrome(service=service)
    driver.get(url)
    time.sleep(10)
    driver.find_element_by_class_name('s_ipt').send_keys(key_word)
    time.sleep(5)
    driver.find_element_by_class_name('s_btn_wr').click()
    time.sleep(5)
    content = driver.page_source.encode('utf-8')
    driver.close()
    soup = BeautifulSoup(content, 'lxml')
    return soup

这一段代码名叫"driver_open(key_word)",其输入是main中的关键词。它的目的主要是通过模拟人类使用浏览器的方法,将想要搜索的关键词输入到百度文库的搜索框内,在一定的时间之后,点击搜索,并将页面内容按照“utf-8”的格式保存,最后将得到的内容用lxml形式解析。它主要做了这几件事:

  1. 打开本地的chromedriver
  2. 模拟人类操作,搜索相关内容
  3. 保存内容,并关闭Chromedriver
  4. 将内容按照lxml方式解析成名称为soup的树形结构内容并返回soup。

在这段代码中,我基于学长的代码,做了如下修改:因为我使用的是python3.10版本,这个版本的bs4库需要先使用service来打开Chrome driver。

在模拟浏览器访问的过程中,可以使用time.sleep()休眠一定的时间。防止反爬虫机制将IP封掉。

获取百度学术搜索信息后,我们发现搜索结果并不能在一页中显示完整,百度学术通过多个页来显示信息,而且每个页都有一个url,url的格式也很有规律。现在我们需要访问所有的页,然后逐一提取每一页的学术文档的链接。这里我们用两种方式如下所示:

版本一:假设我们提前知道了访问的总页数,比如是72页。我们就可以根据每一页URL的格式上的规律(见代码),生成每一页的URL然后进行逐个访问。

#自定义页数
def page_url_list(soup, maxpage=0):
    fir_page = ("http://xueshu.baidu.com" + soup.find_all("a", class_="n")[0]["href"])
    urls_list = []
    fail_list = []
    for i in range(maxpage):
        this_page = fir_page.replace("pn=10", "pn={:d}".format(i * 10))
        response = requests.get(this_page)
        soup_new = BeautifulSoup(response.text, "lxml")
        c_fonts = soup_new.find_all("h3", class_="t c_font")
        for c_font in c_fonts:
            url = c_font.find("a").attrs["href"]
            urls_list.append(url)
        if len(soup_new.find_all("a", class_="n", style="margin-right: 19px;")) == 0:
            fail_list.append(this_page)
        print("找到第" + str(i + 1) + "页,href中pn值为" + this_page[this_page.index("pn=") + 3 : this_page.index("&tn")])
        print(this_page)
        print()
    print("失败页面")
    for i in range(len(fail_list)):
        print(fail_list[i])
    set(urls_list)
    print("链接总数量")
    print(len(urls_list))
    return urls_list

版本一:假设不知道搜索结果一共有多少页。我们先通过第一页的页面底部的“下一页”按钮访问下一页,并循环此过程,直到不再出现“下一页”按钮。

#自动停止
def page_url_list_2(soup, maxpage=0):
    fir_page = ("http://xueshu.baidu.com" + soup.find_all("a", class_="n")[0]["href"]).replace("pn=10", "pn=0")
    next_page = fir_page
    urls_list = []
    maxtime = 100
    for i in range(maxpage):
        for j in range(maxtime):
            response = requests.get(next_page)
            soup_new = BeautifulSoup(response.text, "lxml")
            c_fonts = soup_new.find_all("h3", class_="t c_font")
            for c_font in c_fonts:
                url = c_font.find("a").attrs["href"]
                urls_list.append(url)
            print("找到第" + str(i + 1) + "页,href中pn值为" + next_page[next_page.index("pn=") + 3 : next_page.index("&tn")])
            if len(soup_new.find_all("a", class_="n", style="margin-right: 19px;")) != 0:
                break;
            print("第" + str(j + 1) + "次访问第" + str(i + 1) + "页未找到下一页...")
            if j == maxtime - 1:
                print("在第" + str(i + 2) + "页停止")
                print()
                set(urls_list)
                print("链接总数量")
                print(len(urls_list))
                return urls_list
        next_page = "http://xueshu.baidu.com" + soup_new.find_all("a", class_="n", style="margin-right: 19px;")[0]["href"]
        print(next_page)
        print(soup_new.find_all("a", class_="n", style="margin-right: 19px;"))
        print()
    set(urls_list)
    print("链接总数量")
    print(len(urls_list))
    return urls_list

上面两个版本的访问页面方式不太相同,一个是通过URL格式的规律来找到每一页的URL;另一种是通过加载新页面后获得的底部的按钮获得下一页的URL。

需要注意的是:在实际操作中,我发现有时候某些页面访问一次会无法正常返回信息。这就会导致在提取信息的过程中某些页面的学术文档链接缺失。

解决这个问题的方法有两个:一是将不能正常访问的百度学术搜索结果页面URL保存起来,之后再次遍历且访问。二是设置一个访问次数上限,比如20次,那么在遇到每个不能正常访问的页面时,我们都继续访问它20次,直到它返回正常信息。

尽管我们有办法解决页面非正常返回的问题,我们也很很难避免频繁访问使得百度将IP封掉。我们可以时常更换IP或者设置每次访问时间间隔(但是这会导致爬取数据时间过长)

def get_item_info(url):
    content_details = requests.get(url)
    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 = ''.join(list(soup.select('div.abstract_wr p.abstract')[0].stripped_strings)[0].replace("\u3000", ' '))
    except(IndexError):
        abstract = ''
    try:
        key_words = ''
        soup1 = BeautifulSoup(str(soup.select('#dtl_l > div.main-info > div.c_content > div.kw_wr > p.kw_main')[0]), "lxml")
        a_list = soup1.find_all('a')
        for a in a_list:
            key_words = key_words + str(a.get_text()).replace(";",";") + ";"
    except(IndexError):
        key_words = ''
    return title,  abstract,  key_words

这一段目的是对每个学术文档的URL进行访问并提取相关的关键词、摘要、标题信息,并将它们返回。

可以看到,这里使用了很对try catch语句,防止访问过程中由于提取不到数据的错误使得程序完全中断。

 def save_csv(urls_list):
    titles = []
    abstracts = []
    key_words = []
    count = 0
    for url in urls_list:
        count += 1
        print("正在提取第" + str(count) + "篇论文")
        title, abstract, key_word = get_item_info(url)
        if title == '' or abstract == '' or key_word == '':
            continue
        titles.append(title)
        abstracts.append(abstract)
        key_words.append(key_word)
    dit = {}
    dit["titles"] = titles
    dit["abstracts"] = abstracts
    dit["key_words"] = key_words
    data = pd.DataFrame(dit)
    columns = ["titles", "abstracts", "key_words"]
    data.to_csv("abstract_data_2.csv", index=False, columns=columns)
    print("完成!")

这里将获取的数据转换成Data Frame格式,然后再转换成csv格式保存下来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值