python-在合理的时间内使用登录擦除多个URL

时间:2019-05-15 06:53:03

标签: python asynchronous python-requests screen-scraping python-requests-html

我正在尝试从需要登录才能查看实际内容的网站上抓取一些数据。一切正常,但是每个请求大约需要5秒钟,这对于我的需求来说是很慢的(> 5000个URL会被抓取)。似乎有更快的方法,例如asyncio aiohttp模块。 但是,我在网上找到的所有示例都没有显示如何登录到站点然后使用这些工具。

因此,我基本上需要一个易于遵循的示例来完成此操作。

我尝试重建此示例: https://realpython.com/python-concurrency/#what-is-concurrency 与我的代码,这是行不通的。我还尝试了来自request_html的AsyncHTMLSession(),该函数返回了一些内容,但似乎不记得登录了。

到目前为止,这是我的代码:

[
  {
    "outputFileName": "wwwroot/vendor.min.css",
    "inputFiles": [
      "node_modules/bootstrap/dist/css/bootstrap.min.css",
      "node_modules/font-awesome/css/font-awesome.css",
      "node_modules/font-awesome/css/font-awesome.min.css",
      "node_modules/ckeditor/contents.css"

    ],
    "minify": { "enabled": false }
  },
  {
    "outputFileName": "wwwroot/vendor.min.js",
    "inputFiles": [
      "node_modules/jquery/dist/jquery.min.js",
      "node_modules/popper.js/dist/umd/popper.min.js",
      "node_modules/bootstrap/dist/js/bootstrap.min.js",
      "node_modules/ckeditor/ckeditor.js"

    ],
    "minify": { "enabled": false }
  }
]

2 个答案:

答案 0 :(得分:0)

查看asyncio并使用asyncio.gather函数。

在方法中将“ links = [几个网址]”行下的所有内容包装起来。

请注意,这不是线程安全的,因此请勿在方法内更改变量。

这也是线程化的,因此使用asyncio.sleep(randint(0,2))来延迟某些线程可能很有用,因此它不能同时触发所有线程。

然后使用asyncio用下面的新网址调用下面的方法

tasks =[]
for url in urls:
    tasks.append(wrapped_method(url))

results = asyncio.gather(*tasks)

希望有帮助。

否则请看https://github.com/jreese/aiomultiprocess

答案 1 :(得分:0)

您似乎已经在使用SWITCH,因此可以尝试requests-async。下面的示例将帮助您解决问题的“ ”部分,只需相应地调整requests函数以搜索HTML标记即可。默认情况下,它将并行运行50个请求(parse_html),以不耗尽系统上的资源(文件描述符等)。

示例:

MAX_REQUESTS

输出:

import asyncio
import requests_async as requests
import time

from bs4 import BeautifulSoup
from requests_async.exceptions import HTTPError, RequestException, Timeout


MAX_REQUESTS = 50
URLS = [
    'http://envato.com',
    'http://amazon.co.uk',
    'http://amazon.com',
    'http://facebook.com',
    'http://google.com',
    'http://google.fr',
    'http://google.es',
    'http://google.co.uk',
    'http://internet.org',
    'http://gmail.com',
    'http://stackoverflow.com',
    'http://github.com',
    'http://heroku.com',
    'http://djangoproject.com',
    'http://rubyonrails.org',
    'http://basecamp.com',
    'http://trello.com',
    'http://yiiframework.com',
    'http://shopify.com',
    'http://airbnb.com',
    'http://instagram.com',
    'http://snapchat.com',
    'http://youtube.com',
    'http://baidu.com',
    'http://yahoo.com',
    'http://live.com',
    'http://linkedin.com',
    'http://yandex.ru',
    'http://netflix.com',
    'http://wordpress.com',
    'http://bing.com',
]


class BaseException(Exception):
    pass


class HTTPRequestFailed(BaseException):
    pass


async def fetch(url, timeout=5):
    async with requests.Session() as session:
        try:
            resp = await session.get(url, timeout=timeout)
            resp.raise_for_status()
        except HTTPError:
            raise HTTPRequestFailed(f'Skipped: {resp.url} ({resp.status_code})')
        except Timeout:
            raise HTTPRequestFailed(f'Timeout: {url}')
        except RequestException as e:
            raise HTTPRequestFailed(e)
    return resp


async def parse_html(html):
    bs = BeautifulSoup(html, 'html.parser')
    if not html: print(html)
    title = bs.title.text.strip()
    return title if title else "Unknown"


async def run(sem, url):
    async with sem:
        start_t = time.time()
        resp = await fetch(url)
        title = await parse_html(resp.text)
        end_t = time.time()
        elapsed_t = end_t - start_t
        r_time = resp.elapsed.total_seconds()
        print(f'{url}, title: "{title}" (total: {elapsed_t:.2f}s, request: {r_time:.2f}s)')
        return resp


async def main():
    sem = asyncio.Semaphore(MAX_REQUESTS)
    tasks = [asyncio.create_task(run(sem, url)) for url in URLS]
    for f in asyncio.as_completed(tasks):
        try:
            result = await f
        except Exception as e:
            print(e)


if __name__ == '__main__':
    asyncio.run(main())

现在,这可能仍然无法帮助您解决日志问题。您要查找的HTML标记(或整个网页)可以由JavaScript生成,因此您将需要# time python req.py http://google.com, title: "Google" (total: 0.69s, request: 0.58s) http://yandex.ru, title: "Яндекс" (total: 2.01s, request: 1.65s) http://github.com, title: "The world’s leading software development platform · GitHub" (total: 2.12s, request: 1.90s) Timeout: http://yahoo.com ... real 0m6.868s user 0m3.723s sys 0m0.524s 之类的工具,该工具使用无头浏览器来读取JavaScript呈现的内容。

您的登录表单也可能使用CSRF保护,例如登录Django admin后端的示例:

requests-html

我们使用会话首先执行获取请求,从>>> import requests >>> s = requests.Session() >>> get = s.get('http://localhost/admin/') >>> csrftoken = get.cookies.get('csrftoken') >>> payload = {'username': 'admin', 'password': 'abc123', 'csrfmiddlewaretoken': csrftoken, 'next': '/admin/'} >>> post = s.post('http://localhost/admin/login/?next=/admin/', data=payload) >>> post.status_code 200 cookie中获取令牌,然后使用两个隐藏的表单字段登录:

csrftoken

注意:示例使用的是Python 3.7 +

相关问题