【教程】爬取和统计Google Scholar上指定关键词的文章信息

news2024/11/22 14:35:02

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn]

目录

背景介绍

未来改进

参考代码


背景介绍

  • 通过自动点击页面来抓取文章信息。这个脚本对于用来看某个关键词在近几年的研究趋势很有用~
  • 半自动:当遇到谷歌人机验证,需要手动完成。
  • 注意将selenium升级到最新版本,他会自动下载chrome内核。
  • 可对脚本修改,来抓取更多数据、或者统计更多信息。
  • 注释非常详细;

未来改进

  • 抓取一页,写入一页,免得中途崩溃数据全没;
  • 更多异常页面检测(目前很少遇到);
  • 抓取和整理更多信息;
  • 记录坐标,断点续抓;
  • 更换ip;

参考代码

import random
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import re
from tqdm import tqdm
import pyautogui
from bs4 import BeautifulSoup
import lxml
import pandas as pd
from enum import Enum

class Errors(Enum):
    SUCCESS         = '成功'
    SERVER_ERROR    = '服务器错误'


class Scholar:
    def __init__(self) -> None:
        self.driver = None
        self.results = []

    def start_browser(self, wait_time=10):
        # 创建ChromeOptions对象
        options = Options()
        # 启用无头模式
        # options.add_argument("--headless")
        # 启用无痕模式
        options.add_argument("--incognito")
        options.add_argument("--disable-domain-reliability")
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_argument("--disable-client-side-phishing-detection")
        options.add_argument("--no-first-run")
        options.add_argument("--use-fake-device-for-media-stream")
        options.add_argument("--autoplay-policy=user-gesture-required")
        options.add_argument("--disable-features=ScriptStreaming")
        options.add_argument("--disable-notifications")
        options.add_argument("--disable-popup-blocking")
        options.add_argument("--disable-save-password-bubble")
        options.add_argument("--mute-audio")
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-gpu")
        options.add_argument("--disable-extensions")
        options.add_argument("--disable-software-rasterizer")
        options.add_argument("--disable-dev-shm-usage")
        options.add_argument("--disable-webgl")
        options.add_argument("--allow-running-insecure-content")
        options.add_argument("--no-default-browser-check")
        options.add_argument("--disable-full-form-autofill-ios")
        options.add_argument("--disable-autofill-keyboard-accessory-view[8]")
        options.add_argument("--disable-single-click-autofill")
        options.add_argument("--ignore-certificate-errors")
        options.add_argument("--disable-infobars")
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_argument("--disable-blink-features")
        # 禁用实验性QUIC协议
        options.add_experimental_option("excludeSwitches", ["enable-quic"])
        # 创建Chrome浏览器实例
        self.driver = webdriver.Chrome(options=options)
        # 等待页面加载完成
        self.driver.implicitly_wait(wait_time)


    def __search_onepage(self):
        """爬取当前页面文章的的信息"""
        results = []

        if not self.check_element_exist(check_type='ID', value='gs_res_ccl_mid'):
            print('>> 当前页面不存在文章列表')
            return []
        gs_scl = self.driver.find_element(by=By.ID, value='gs_res_ccl_mid').find_elements(by=By.CLASS_NAME, value='gs_scl')
        for i, item in tqdm(enumerate(gs_scl)):
            gs_rt = item.find_element(by=By.CLASS_NAME, value='gs_rt')
            gs_a = item.find_element(by=By.CLASS_NAME, value='gs_a')
            gs_rt_a = gs_rt.find_element(by=By.TAG_NAME, value='a') if self.check_element_exist(check_type='TAG_NAME', value='a', source=gs_rt.get_attribute('innerHTML')) else None
            publisher_info = gs_a.text.strip().replace('\n', '')
            # 论文标题
            title = gs_rt.text.strip().replace('\n', '').split(']')[-1].strip()
            # 论文链接
            href = gs_rt_a.get_attribute('href') if gs_rt_a else ''
            # 发表年份
            year = re.findall(r'\d{4}', publisher_info)[-1]
            
            # print(f'[{i}] {title} => {href} => {publisher_info} => {year}')
            results.append({'title': title, 'href':href, 'year': year})
        return results

    def check_element_exist(self, value, check_type='CLASS_NAME', source=None) -> bool:
        """检查页面是否存在指定元素"""
        page_source = source if source else self.driver.page_source
        soup = BeautifulSoup(page_source, 'lxml')
        if check_type == 'ID':
            return len(soup.find_all(id=value)) != 0
        elif check_type == 'CLASS_NAME':
            return len(soup.find_all(class_=value)) != 0
        elif check_type == 'TAG_NAME':
            return len(soup.find_all(value)) != 0
        elif check_type == 'FULL':
            return value in page_source
        else:
            print(f'>> 检查条件[{check_type}]不对')
        return False

    def check_captcha(self) -> bool:
        """检查是否需要人机验证;一个是谷歌学术的、一个是谷歌搜索的"""
        return self.check_element_exist(check_type='ID', value='gs_captcha_f') or \
               self.check_element_exist(check_type='ID', value='captcha-form')


    def process_error(self, error: Errors) -> bool:
        """尽可能尝试解决错误"""
        success = False
        if error == Errors.SERVER_ERROR:
            pass
        
        return success
    
    
    def check_error(self, try_solve = True) -> Errors:
        """检查当前页面是否出错"""
        error = Errors.SUCCESS
        if self.check_element_exist(check_type='FULL', value='服务器错误'):
            error = Errors.SERVER_ERROR
        
        # 尝试解决错误
        if try_solve and error != Errors.SUCCESS:
            error = Errors.SUCCESS if self.process_error(error) else error
        return error


    def __scroll2bottom(self):
        # 将滚动条移动到页面的底部
        self.driver.switch_to.default_content()
        js = "var q=document.documentElement.scrollTop=100000"
        self.driver.execute_script(js)
        
        
    def search(self, keywords, max_pages=100, delay=0):
        keywords = keywords.replace(' ', '+')
        url = f'https://scholar.google.com/scholar?hl=zh-CN&as_sdt=0%2C5&q={keywords}&btnG='
        # 打开Google Scholar网站
        self.driver.get(url)

        for _ in tqdm(range(max_pages), desc='搜索中'):
            while self.check_captcha():
                pyautogui.alert(title='状态异常', text='请手动完成人机验证后,点击“已完成”', button='已完成')
                self.driver.refresh()
                time.sleep(2)
            if self.check_error() != Errors.SUCCESS:
                if pyautogui.confirm(text='请检查页面出现了什么问题;\n解决后,点击“确定”将会重试;\n否则,点击“取消”提前结束脚本;', title='状态异常', buttons=['确定', '取消']) == '取消':
                    print('>> 提前结束')
                    break
                time.sleep(2)
            
            onepage = self.__search_onepage()
            if not onepage:
                print('>> 当前页为空, 重试')
                self.driver.refresh()
                time.sleep(2)
                continue
            if not self.check_element_exist(check_type='CLASS_NAME', value='gs_ico_nav_next'):
                print('>> 全部结束')
                break
            self.__scroll2bottom()
            time.sleep(0.1)
            self.driver.find_element(by=By.CLASS_NAME, value="gs_ico_nav_next").click()
            self.results.extend(onepage)
            time.sleep(delay)
        return self.results

    def close_browser(self):
        # 关闭浏览器
        self.driver.quit()
    
    def save_file(self, filename='scholar.csv', nodup=False):
        unique_data = self.results
        if nodup:
            # 根据href字段进行去重
            unique_data = [dict(t) for t in {tuple(d.items()) for d in unique_data}]
        print(f'>> 去重效果:{len(self.results)} => {len(unique_data)}')
        pd.DataFrame(unique_data).to_excel(filename, index=False, encoding='utf-8')

    def statistical_information(self):
        pass

if __name__ == '__main__':
    keywords = input('>> 请输入搜索关键词: ').strip() or 'Wireless Sensor Networks'
    scholar = Scholar()
    scholar.start_browser(wait_time=60)
    results = scholar.search(keywords, max_pages=1, delay=random.randint(0, 0)) 
    scholar.close_browser()
    scholar.save_file(filename=f'output_{"_".join(keywords.split())}.xlsx', nodup=True)
    print('>> all done <<')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/748568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP MIRO发票过账报错 这个项目不允许输入税

SAP MIRO发票过账报错 这个项目不允许输入税 J3税额0.01为手动输入 原因是含税金额0.08 * J3对应税率&#xff0c;结果保留2位小数大小&#xff0c;系统不允许输入税额

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(四)

UIAbility组件与UI的数据同步 基于HarmonyOS的应用模型&#xff0c;可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步。 1.EventHub&#xff1a;基于发布订阅模式来实现&#xff0c;事件需要先订阅后发布&#xff0c;订阅者收到消息后进行处理。 2.globalThis&…

XSS靶场【1-10关】攻击攻略

文章目录 第一关第二关第三关第四关第五关第六关第七关第八关第九关第十关 通过&#xff08;xss-labs-master&#xff09;[]PHPStudy搭建的XSS靶场1-10关的攻击教程需要下载好XSS靶场源码&#xff0c;导入到phpstudy的WWW目录下攻击主机需要安装BurpSuit、FoxyProxy浏览器插件&…

第三十章:数据库其他调优策略

第三十章&#xff1a;数据库其他调优策略 30.1&#xff1a;数据库调优的措施 调优的目标 尽可能的节省系统资源&#xff0c;以便系统可以提供更大负荷的服务(吞吐量更大)。合理的结构设计和参数调整&#xff0c;以提高用户操作响应的速度。(响应速度更快)。减少系统的瓶颈&…

Win32汇编对话框子控件复习学习

在此已经做了Win32汇编的对话框子控件ListBox和ComboBox&#xff1b; Win32汇编ListBox最简Demo_win32 list列表_bcbobo21cn的博客-CSDN博客 Win32汇编最简ComboBox Demo_bcbobo21cn的博客-CSDN博客 它们的代码是相似的&#xff1b; 以ComboBox为例&#xff1b; 首先是要…

java 如何快速实现异步调用方法

java 如何快速实现异步调用方法 什么是异步编程CompletableFuturejava 演示 什么是异步编程 在实现异步调用之前&#xff0c;我们先了解一下&#xff0c;什么是异步编程&#xff1f;什么场景下适用等等情况。 我们都知道&#xff0c;在传统的同步编程中&#xff0c;当一个操作…

Linux——深度解析IO文件流缓冲区问题(原理详解+代码手撕)

深度剖析缓存区 1.&#x1f4ad;缓冲区介绍1.1&#x1f4ab;什么是缓冲区1.2&#x1f4ab;缓冲区有什么用1.3 &#x1f4ab;缓冲区的初步认识 2.&#x1f4ad;缓存区测试3.&#x1f951;缓冲区是谁提供的3.1测试 一下 在fork&#xff08;&#xff09;之前刷新缓冲区会怎么样&am…

avue 表单绑定值;avue表单项根据某项的值去联动显隐或是联动下拉数据;avue select切换与另外一个select的options联动

效果&#xff1a;发布type为shp时 数据相关的都隐藏&#xff0c;当发布type为postgis时则显示 1.avue表单绑定值 html <avue-form :option"option" v-model"publishForm"></avue-form> js data中定义 data() {return {publishForm: {},optio…

Go语言基础教程:变量、基本数据类型、输出、注释、运算符、if-else条件判断、函数

文章目录 一、变量的使用1.1 定义变量1.2 常量1.3 变量的赋值与内存相关 二、变量基本类型2.1 有符号整型2.2 无符号整型2.3 string类型2.4 bool类型 三、输出3.1 常用打印功能3.2 格式化输出3.3 内置输出方法与fmt的区别 四、注释五、运算符六、条件语句6.1 基本使用6.2 条件嵌…

12.7 跳转与存储器访问指令

目录 跳转指令 方式一 方式二 方式三 程序返回 ARM指令的条件码 比较指令 内存访问指令&#xff08;一&#xff09; load&#xff08;LD加载&#xff09;/srore&#xff08;ST存储&#xff09;指令&#xff1a;访问&#xff08;读写&#xff09;内存 写内存 读内存 …

leetcode:寻找数组的中心下标

寻找数组的中心下标 easy 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为在下标…

git bash设置字体大小

背景 git bash默认字体太小了&#xff0c;每次读信息都要伸头盯着屏幕&#xff0c;很不自在&#xff0c;不符合我的风格&#xff0c;so let’s do it&#xff01; 修改前的git bash&#xff1a; 正确的打开方式 1、在任意目录下&#xff0c;右键选择“Git Bash Here”&…

这5款能带来意想不到效果的小工具,你用过几款?

​ 有时候一些小工具&#xff0c;能给你带来一些意想不到的效果&#xff0c;我们来看看下面这5款工具&#xff0c;你又用过其中几款呢&#xff1f; 数据标注平台——Labelbox ​ Labelbox是一个数据标注平台&#xff0c;可以帮助你创建和管理高质量的训练数据&#xff0c;用于…

【新版系统架构】系统架构设计师教程全篇知识点提炼

第一章-绪论 架构的定义&#xff1a; 1、架构体现在组件中的一个系统的基本组织、彼此的关系和环境的关系及指导它的设计和发展的原则 2、系统是组织起来完成某一特定功能或一组功能的组件集 3、环境或者上下文决定了对这个系统的开发、运作、政策以及会对系统造成其他影响的…

Map集合以及它的实现类们

Map集合 Map集合的概念理解 在现实生活中&#xff0c;我们经常会碰见一对一标识的例子&#xff1a;如身份证号与个人、IP地址与主机名等。这些一一对应的关系就是映射。在Java之中也能为我们的对象添加一个“一”的对应关系—Map<K,V>集合&#xff0c;位于java.util.Ma…

113.(cesium篇)cesium卷帘分屏效果

地图之家总目录(订阅之前必须详细了解该博客) 地图之家:cesium+leaflet+echart+地图数据+地图工具等相关内容的介绍 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: cesium卷帘分屏效果 下面献上完整代码,代码重要位置会做相应解释…

实战演练Python数据分析[pandas]

文章目录 [TOC](文章目录) 前言一、MoviesLens数据集二、美国1880-2010年的婴儿名字三、美国农业部视频数据库四、2012年联邦选举委员会数据库总结 前言 本篇文章出自于《利用Python进行数据分析》示例数据 请结合提供的示例数据&#xff0c;分析代码的功能&#xff0c;并进行…

微服务优雅上下线的实践方法

导语 本文介绍了微服务优雅上下线的实践方法及原理&#xff0c;包括适用于 Spring 应用的优雅上下线逻辑和服务预热&#xff0c;以及使用 Docker 实现无损下线的 Demo。同时&#xff0c;本文还总结了优雅上下线的价值和挑战。 作者简介 颜松柏 腾讯云微服务架构师 拥有超过…

Win10下安装TensorRT

Win10下安装TensorRT 前言相关介绍Win10下安装TensorRT查看cuda版本下载tensorrt8.xx版本&#xff0c;适用于Windows的cuda11.x的版本解压下载好的压缩包使用pip下载wheel文件遇到新问题解决方法 测试TensorRT是否安装成功 参考 前言 由于本人水平有限&#xff0c;难免出现错漏…

WAS 9.0 ND 命令行安装-基于LINUX 8

WAS 9.0 安装文件准备如下&#xff1a; gtk.x86_64_1.8.9004.20190423_2015.zip ----IM安装源文件 sdk.repo.8035.java8.linux.zip ----JAVA安装源文件 was.repo.90501.nd.zip ----WAS安装源文件 …