网页抓取 Google 搜索结果

3 python beautifulsoup web-crawler web-scraping

我正在逐页抓取 Google Scholar 搜索结果。在一定数量的页面后,会弹出验证码并中断我的代码。我读到 Google 限制了我每小时可以发出的请求。有没有办法绕过这个限制?我阅读了一些关于 API 的内容,但我不确定这是否有帮助。

Mat*_*ela 13

自从我过去从谷歌上爬过之后,我感受到了你的痛苦。为了完成我的工作,我尝试了以下方法。此列表从最简单到最难的技术排序。

  • 限制您的每秒请求数: Google 和许多其他网站会识别每秒来自同一台机器的大量请求,并自动阻止它们,作为抵御拒绝服务攻击的防御措施。您需要做的就是保持温和,例如每 1-5 秒仅执行 1 个请求,以避免被快速禁止。
  • 随机化你的睡眠时间:让你的代码睡眠恰好 1 秒太容易被检测为脚本。在每次迭代时让它休眠随机的时间。这个 StackOverflow 答案显示了一个关于如何随机化它的例子。
  • 使用启用了 cookie 的网络抓取工具库:如果您从头开始编写抓取代码,Google 会注意到您的请求不会返回它收到的 cookie。使用一个好的库,比如Scrapy来规避这个问题。
  • 使用多个 IP 地址:节流肯定会降低您的抓取吞吐量。如果您确实需要快速抓取数据,则需要使用多个 IP 地址以避免被禁止。有几家公司在 Internet 上以一定的金额提供此类服务。我使用过ProxyMesh并且非常喜欢它们的质量、文档和客户支持。
  • 使用真正的浏览器:如果不处理 JavaScript 或没有图形界面,一些网站会识别您的抓取工具。例如,使用带有Selenium的真实浏览器将解决这个问题。

您还可以查看我的爬虫项目,该项目是为纽约大学的 Web 搜索引擎课程编写的。它本身并不抓取 Google 但包含一些上述技术,例如节流和随机化睡眠时间。


Rus*_*bot 5

来自抓取 Google Scholar 的个人经验。45 秒足以避免验证码和机器人检测。我的爬虫运行了超过 3 天而没有被发现。如果您确实被标记,等待大约 2 小时就足以重新开始。这是我的代码的摘录。

class ScholarScrape():
    def __init__(self):
        self.page = None
        self.last_url = None
        self.last_time = time.time()
        self.min_time_between_scrape = int(ConfigFile.instance().config.get('scholar','bot_avoidance_time'))
        self.header = {'User-Agent':ConfigFile.instance().config.get('scholar','user_agent')}
        self.session = requests.Session()
        pass

    def search(self, query=None, year_lo=None, year_hi=None, title_only=False, publication_string=None, author_string=None, include_citations=True, include_patents=True):
        url = self.get_url(query, year_lo, year_hi, title_only, publication_string, author_string, include_citations, include_patents)
        while True:
            wait_time = self.min_time_between_scrape - (time.time() - self.last_time)
            if wait_time > 0:
                logger.info("Delaying search by {} seconds to avoid bot detection.".format(wait_time))
                time.sleep(wait_time)
            self.last_time = time.time()
            logger.info("SCHOLARSCRAPE: " + url)
            self.page = BeautifulSoup(self.session.get(url, headers=self.header).text, 'html.parser')
            self.last_url = url

            if "Our systems have detected unusual traffic from your computer network" in str(self.page):
                raise BotDetectionException("Google has blocked this computer for a short time because it has detected this scraping script.")

            return

    def get_url(self, query=None, year_lo=None, year_hi=None, title_only=False, publication_string=None, author_string=None, include_citations=True, include_patents=True):
        base_url = "https://scholar.google.com.au/scholar?"
        url = base_url + "as_q=" + urllib.parse.quote(query)

        if year_lo is not None and bool(re.match(r'.*([1-3][0-9]{3})', str(year_lo))):
            url += "&as_ylo=" + str(year_lo)

        if year_hi is not None and bool(re.match(r'.*([1-3][0-9]{3})', str(year_hi))):
            url += "&as_yhi=" + str(year_hi)

        if title_only:
            url += "&as_yhi=title"
        else:
            url += "&as_yhi=any"

        if publication_string is not None:
            url += "&as_publication=" + urllib.parse.quote('"' + str(publication_string) + '"')

        if author_string is not None:
            url += "&as_sauthors=" + urllib.parse.quote('"' + str(author_string) + '"')

        if include_citations:
            url += "&as_vis=0"
        else:
            url += "&as_vis=1"

        if include_patents:
            url += "&as_sdt=0"
        else:
            url += "&as_sdt=1"

        return url

    def get_results_count(self):
        e = self.page.findAll("div", {"class": "gs_ab_mdw"})
        try:
            item = e[1].text.strip()
        except IndexError as ex:
            if "Our systems have detected unusual traffic from your computer network" in str(self.page):
                raise BotDetectionException("Google has blocked this computer for a short time because it has detected this scraping script.")
            else:
                raise ex

        if self.has_numbers(item):
            return self.get_results_count_from_soup_string(item)
        for item in e:
            item = item.text.strip()
            if self.has_numbers(item):
                return self.get_results_count_from_soup_string(item)
        return 0

    @staticmethod
    def get_results_count_from_soup_string(element):
        if "About" in element:
            num = element.split(" ")[1].strip().replace(",","")
        else:
            num = element.split(" ")[0].strip().replace(",","")
        return num

    @staticmethod
    def has_numbers(input_string):
        return any(char.isdigit() for char in input_string)


class BotDetectionException(Exception):
    pass

if __name__ == "__main__":
    s = ScholarScrape()
    s.search(**{
        "query":"\"policy shaping\"",
        # "publication_string":"JMLR",
        "author_string": "gilboa",
        "year_lo": "1995",
        "year_hi": "2005",

    })
    x = s.get_results_count()
    print(x)
Run Code Online (Sandbox Code Playgroud)