1

我以前在 Node.js 中编写过应用程序,特别是数据抓取工具。这些类型的应用程序没有 Web 前端,而只是与 cron 作业定时的进程,以异步发出许多可能很复杂的 HTTP GET 请求来拉取网页,然后从结果中抓取和存储数据。

我可能编写的函数示例如下:

// Node.js

var request = require("request");

function scrapeEverything() {
    var listOfIds = [23423, 52356, 63462, 34673, 67436];

    for (var i = 0; i < listOfIds.length; i++) {
        request({uri: "http://mydatasite.com/?data_id = " + listOfIds[i]},
                function(err, response, body) {
                     var jsonobj = JSON.parse(body);
                      storeMyData(jsonobj);
                });
    }
}

此函数循环遍历 ID 并发出一堆异步 GET 请求,然后从中存储数据。

我现在正在用 Python 编写一个爬虫,并尝试使用 Tornado 做同样的事情,但我在文档中看到的所有内容都是指 Tornado 充当 Web 服务器,这不是我想要的。有人知道怎么做吗?

4

3 回答 3

2

答案比我想的要复杂一些,但它是如何使用 Tornado ioloop 和 AsyncHTTPClient 获取一些数据的快速演示。我实际上已经在 Tornado 中编写了一个网络爬虫,所以它可以“无头”使用。

import tornado.ioloop
import tornado.httpclient

class Fetcher(object):
    def __init__(self, ioloop):
        self.ioloop = ioloop
        self.client = tornado.httpclient.AsyncHTTPClient(io_loop=ioloop)

    def fetch(self, url):
        self.client.fetch(url, self.handle_response)

    @property
    def active(self):
        """True if there are active fetching happening"""

        return len(self.client.active) != 0

    def handle_response(self, response):
        if response.error:
            print "Error:", response.error
        else:
            print "Got %d bytes" % (len(response.body))

        if not self.active:
            self.ioloop.stop()

def main():
    ioloop = tornado.ioloop.IOLoop.instance()
    ioloop.add_callback(scrapeEverything)
    ioloop.start()

def scrapeEverything():
    fetcher = Fetcher(tornado.ioloop.IOLoop.instance())

    listOfIds = [23423, 52356, 63462, 34673, 67436]

    for id in listOfIds:
        fetcher.fetch("http://mydatasite.com/?data_id=%d" % id)

if __name__ == '__main__':
    main()
于 2012-07-18T18:29:14.337 回答
1

如果您对 tornado 的替代方案持开放态度(我假设您使用套接字编程而不是 urllib2),您可能会对异步、并发(和分布式、容错)编程框架asyncoro感兴趣。使用 asyncoro 编程与使用线程非常相似,除了一些语法变化。您的问题可以用 asyncoro 实现为:

import asyncoro, socket

def process(url, coro=None):
    # create asynchronous socket
    sock = asyncoro.AsynCoroSocket(socket.socket())
    # parse url to get host, port; prepare get_request
    yield sock.connect((host, port))
    yield sock.send(get_request)
    body = yield sock.recv()
    # ...
    # process body

for i in [23423, 52356, 63462, 34673, 67436]:
    asyncoro.Coro(process, "http://mydatasite.com/?data_id = %s" % i)
于 2012-07-18T15:12:11.747 回答
0

您还可以尝试不需要任何外部库的本机解决方案。对于 linux,它基于 epoll,可能看起来像这样。使用示例:

# ------------------------------------------------------------------------------------
def sampleCallback(status, data, request):
    print 'fetched:', status, len(data)
    print data

# ------------------------------------------------------------------------------------
fetch(HttpRequest('google.com:80', 'GET', '/', None, sampleCallback))
于 2015-06-10T11:11:27.847 回答