从网站上抓取数据的最佳方法是什么?

0x1*_*ad2 106 api screen-scraping web-scraping

我需要从网站中提取内容,但应用程序不提供任何应用程序编程接口或其他机制来以编程方式访问该数据.

我找到了一个名为Import.io的有用的第三方工具,它提供了用于抓取网页和构建数据集的点击操作功能,唯一的一件事就是我想在本地保存我的数据,而且我不想订阅任何订阅计划.

该公司使用什么样的技术来抓取网页并构建他们的数据集?我发现一些网络抓取框架pjscrapeScrapy可以提供这样的功能

Jes*_*ock 266

您肯定希望从一个良好的Web抓取框架开始.稍后您可能会认为它们太限制了,您可以将自己的库堆放在一起但没有大量的刮擦经验,您的设计将比pjscrape或scrapy更糟糕.

注意:我在这里使用基本可互换的术语爬行和抓取.这是我对你的Quora问题的回答的副本,它很长.

工具

根据您首选的浏览器,熟悉Firebug或Chrome开发工具.当您浏览从中提取数据的网站并确定哪些网址包含您要查找的数据以及构成响应的数据格式时,这将是绝对必要的.

您需要具备HTTP和HTML的良好工作知识,并且可能希望在中间代理软件中找到一个体面的人.您需要能够检查HTTP请求和响应,并了解如何传递cookie和会话信息以及查询参数.Fiddler(http://www.telerik.com/fiddler)和Charles Proxy(http://www.charlesproxy.com/)是很受欢迎的工具.我使用mitmproxy(http://mitmproxy.org/)很多,因为我更像是一个键盘人而不是鼠标人.

某种类型的控制台/ shell/REPL类型环境,您可以通过即时反馈尝试各种代码,这将是非常宝贵的.像这样的逆向工程任务需要大量的试验和错误,因此您需要一个简化工作流程的工作流程.

语言

PHP基本上是out,它不适合这个任务,并且这个领域的库/框架支持很差.Python(Scrapy是一个很好的起点)和Clojure/Clojurescript(令人难以置信的强大和高效但很大的学习曲线)是这个问题的优秀语言.既然你不想学习一门新语言而且你已经知道Javascript我肯定会建议坚持使用JS.我没有使用过pjscrape,但从快速阅读他们的文档看起来相当不错.它非常适合并实现了我在下面描述的问题的出色解决方案.

关于正则表达式的注释:不要将常规表达式用于PARSE HTML.许多初学者都这样做是因为他们已经熟悉了正则表达式.这是一个巨大的错误,使用xpath或css选择器来导航html并且只使用正则表达式从html节点内的实际文本中提取数据.这对你来说可能已经很明显了,如果你尝试的话很快就会很明显,但很多人因为某种原因浪费了很多时间.不要害怕xpath或css选择器,它们比正则表达式更容易学习,它们旨在解决这个问题.

Javascript重型网站

在过去,您只需要发出http请求并解析HTML响应.现在,您几乎肯定必须处理混合标准HTML HTTP请求/响应和目标站点的javascript部分进行的异步HTTP调用的站点.这是您的代理软件和firebug/devtools的网络选项卡非常方便的地方.对这些的回复可能是html或者它们可能是json,在极少数情况下它们将是xml或其他东西.

这个问题有两种方法:

低级方法:

您可以弄清楚网站javascript正在调用的ajax网址以及这些响应的外观并自行提出相同的请求.因此,您可以从http://example.com/foobar中提取html 并提取一段数据,然后从http://example.com/api/baz?foo=b ...中提取json响应得到另一条数据.您需要知道传递正确的cookie或会话参数.这是非常罕见的,但偶尔ajax调用的一些必需参数将是在网站的javascript中进行一些疯狂计算的结果,逆向工程这可能很烦人.

嵌入式浏览器方法:

为什么你需要弄清楚html中的数据和ajax调用中的数据?管理所有会话和cookie数据?您不必在浏览网站时,浏览器和网站javascript就这样做.这就是重点.

如果您只是将页面加载到像phantomjs这样的无头浏览器引擎中,它将加载页面,运行javascript并告诉您所有ajax调用何时完成.如有必要,您可以注入自己的javascript来触发相应的点击或触发网站javascript加载适当数据所需的任何内容.

你现在有两个选择,让它吐出完成的html并解析它或者在你的解析和数据格式化的页面中注入一些javascript并将数据吐出(可能是json格式).您也可以自由混合这两个选项.

哪种方法最好?

这取决于您,您需要熟悉并熟悉低级方法.嵌入式浏览器方法适用于任何事情,它将更容易实现,并将使一些最棘手的刮取问题消失.这也是你需要了解的相当复杂的机器.它不仅仅是HTTP请求和响应,它的请求,嵌入式浏览器呈现,站点javascript,注入的javascript,您自己的代码以及与嵌入式浏览器进程的双向交互.

由于渲染开销,嵌入式浏览器的规模也要慢得多,但除非你要抓取很多不同的域,否则几乎肯定无关紧要.在单个域的情况下,您需要对请求进行速率限制将使渲染时间完全忽略不计.

速率限制/机器人行为

你需要非常清楚这一点.您需要以合理的价格向目标域提出请求.在抓取网站时,您需要编写一个表现良好的机器人,这意味着尊重robots.txt,而不是通过请求锤击服务器.这里的错误或疏忽是非常不道德的,因为这可以被视为拒绝服务攻击.可接受的费率取决于您的要求,1req/s是Google抓取工具运行的最大值,但您不是Google,而且您可能不像Google那样受欢迎.保持尽可能慢.我会建议每页请求之间2-5秒.

使用标识机器人的用户代理字符串识别您的请求,并为您的机器人提供一个网页,说明其目的.此URL位于代理字符串中.

如果网站想阻止您,您将很容易阻止.一个聪明的工程师可以很容易地识别机器人,他们最后几分钟的工作可能会导致数周的工作改变你的刮擦代码,或者只是让它变得不可能.如果这种关系具有敌意,那么目标站点的智能工程师就可以完全阻止编写爬虫的天才工程师.刮痧代码本身就很脆弱,很容易被利用.无论如何,引起这种反应的东西几乎肯定是不道德的,所以写一个表现良好的机器人并不担心这一点.

测试

不是单元/集成测试人员?太糟糕了.你现在必须成为一个.网站经常更改,您将经常更改代码.这是挑战的很大一部分.

在抓取现代网站时涉及很多活动部分,良好的测试实践将有很大帮助.编写此类代码时遇到的许多错误都是以静默方式返回损坏数据的类型.如果没有良好的测试来检查回归,您会发现您已经将无用的损坏数据保存到您的数据库一段时间而没有注意到.这个项目将使您非常熟悉数据验证(找到一些好的库)和测试.没有太多其他问题需要综合测试并且很难测试.

测试的第二部分涉及缓存和更改检测.在编写代码时,您不希望无缘无故地反复敲击同一页面的服务器.在运行单元测试时,您想知道您的测试是否因为您的代码损坏或者网站已经过重新设计而失败.针对所涉及的URL的缓存副本运行单元测试.缓存代理在这里非常有用,但配置和正确使用很棘手.

您还想知道该网站是否已更改.如果他们重新设计了网站并且您的抓取工具损坏了,那么您的单元测试仍会通过,因为它们是针对缓存副本运行的!您将需要另一个较小的集成测试集,这些集合测试不常在实时站点上运行,或者需要在爬网代码中进行良好的日志记录和错误检测,以记录确切的问题,提醒您解决问题并停止爬网.现在,您可以更新缓存,运行单元测试并查看需要更改的内容.

法律问题

如果你做蠢事,这里的法律可能会有点危险.如果涉及到法律,那么您正在与那些经常将wget和curl称为"黑客工具"的人打交道.你不想要这个.

这种情况的道德现实是,使用浏览器软件请求网址并查看某些数据并使用您自己的软件请求网址并查看某些数据之间没有区别.谷歌是世界上最大的刮刮公司,他们深受喜爱.在用户代理中识别您的机器人名称并对您的网络抓取工具的目标和意图保持开放将有助于此,因为法律了解Google的用途.如果您正在做任何阴暗的事情,比如创建虚假的用户帐户或访问您不应该访问的网站区域(由robots.txt"阻止"或由于某种授权漏洞利用),那么请注意您正在做一些不道德的事情法律对技术的无知在这里将是非常危险的.这是一个荒谬的情况,但它是一个真实的.

实际上可以尝试建立一个新的搜索引擎作为一个正直的公民,犯错误或在你的软件中有一个错误,并被视为黑客.考虑到当前的政治现实,不是你想要的东西.

无论如何,我是谁来写这个巨大的文本墙?

我在生活中写了很多网络抓取相关代码.作为一名顾问,员工和创业公司创始人,我作为网络相关的软件开发已经做了十多年.早期的时候是写perl crawlers/scraper和php网站.当我们嵌入隐藏的iframe时,在Jesse James Garrett将其命名为ajax之前,将csv数据加载到网页中以执行ajax,之后XMLHTTPRequest就是一个想法.在jQuery之前,在json之前.我在30岁左右,这显然对这项业务来说很古老.

我曾两次编写大规模爬行/抓取系统,一次是针对媒体公司(Perl)的大型团队,最近是一个小团队作为搜索引擎启动的CTO(使用Python/Javascript).我目前是一名顾问,主要编写Clojure/Clojurescript(一般来说是一种很棒的专家语言,并且有一些库让爬虫/刮刀问题成为一种乐趣)

我也写过成功的反爬行软件系统.如果您想要识别和破坏您不喜欢的机器人,那么编写难以打开的网站非常容易.

我比编写任何其他类型的软件更喜欢编写爬虫,刮刀和解析器.它具有挑战性,有趣,可用于创造惊人的东西.

  • 你可能是对的,但我知道*I*在PHP中不能轻易做到这一事实.在离开PHP之前,我有近十年的专业PHP经验.我花了一年多的时间在Python上大规模构建一个刮擦系统,如果没有PHP中没有的一些优秀的库,或者没有Python中提供的简洁的元编程技术,我无法想象.这也是我转向Clojure的原因,以获得更强大的元编程能力. (5认同)
  • 我曾经同意你的观点,认为PHP是一个糟糕的选择,但是使用正确的库并不是太糟糕.正则表达式和数组/ sting操作是笨拙的,但从好的方面来说,它是快速的,无处不在. (4认同)
  • Enlive以及Clojure本身对项目特定代码的强大功能是最大的赢家.Schema是一个很好的验证库,它是信息提取代码的重要组成部分.我现在非常满意与Mahout以及Nashorn/Rhino之类的Java世界的简单互操作,用于执行某些类型的js.而Clojure人是那些写这样的libs的类型https://github.com/shriphani/subotai所以你不必这样做....继续下一个评论...... (4认同)
  • 在这样的环境中,有一些图书馆让这件事变得很愉快,很多事情让它变得非常简单和容易......为什么你会满足于"不太糟糕".我同意,它在PHP(以及FORTRAN,C,VB等)中是可行的,但除非你的问题非常简单,否则使用正确的工具来做这个工作会更好.而且,除非你有一个非常简单的问题需要解决......正则表达式到处都有什么关系?安装库比几乎每个抓取问题都简单得多.实际上,对于这个问题,正则表达式通常很慢. (3认同)
  • 我还发现,当你真的需要一个真正的浏览器并且需要使用phantomjs/casperjs时,使用clojurescript(通常使用cljx在clj和cljs之间共享的代码)编写你注入页面的js而不是clojurescript真的很棒.Core.async非常适合协调服务器上的高度并发爬行代码以及在js环境中退出回调地狱(使用core.async cljs协调浏览器自动化与phantomjs中的代码相比,天堂与替代方案相比). (3认同)
  • @mat_dw这是Clojure libs列表的后期添加,但由于这相当受欢迎,我将补充一点,如果你必须摄取包括HTML在内的任何网络内容,我建议使用Aleph over Http-Kit.它支持背压(如果有人发送数据)和新版本(此帖子的时间为0.4.0)版本使用足够新版本的Netty,您可以轻松添加代理以及其他Netty管道变换. (3认同)
  • 对于实际爬行,我仍然试图找到我最喜欢的工具集,从头开始自定义爬虫http-kit + core.async非常棒.当我需要一个更标准的爬虫使用Nutch进行爬行并且Clojure libs进行处理时非常成功. (2认同)
  • @ user3639782,我现在没有任何好的代码发布,到目前为止所有工作都是合同而不是我所有.我现在正在开发一个刮擦库项目,它将是开源的并且同时包含Clojure和Clojurescript代码,但它还处于早期阶段.当我向github提出第一组提交时,我已经给自己做了一个说明,希望能在一两个月内完成. (2认同)

Ehv*_*nce 21

是的,你可以自己做.这只是抓住页面的来源并按照您想要的方式解析它们的问题.

有各种可能性.一个好的组合是使用python-requests(构建在urllib2之上,它urllib.request在Python3中)和BeautifulSoup4,它有选择元素的方法,也允许CSS选择器:

import requests
from BeautifulSoup4 import BeautifulSoup as bs
request = requests.get("http://foo.bar")
soup = bs(request.text) 
some_elements = soup.find_all("div", class_="myCssClass")
Run Code Online (Sandbox Code Playgroud)

有些人更喜欢xpath解析或类似jquery的pyquery,lxml或其他东西.

当您想要的数据由某些JavaScript生成时,上述操作将无效.你需要python-ghost或Selenium.我更喜欢后者与PhantomJS相结合,更轻巧,更易于安装,并且易于使用:

from selenium import webdriver
client = webdriver.PhantomJS()
client.get("http://foo")
soup = bs(client.page_source)
Run Code Online (Sandbox Code Playgroud)

我会建议你开始自己的解决方案.你会理解Scrapy的好处.

ps:看看scrapely:https://github.com/scrapy/scrapely

pps:看看Portia,开始直观地提取信息,无需编程知识:https://github.com/scrapinghub/portia