Python - очистить много URL с логином в разумные сроки - PullRequest
0 голосов
/ 15 мая 2019

Я пытаюсь собрать некоторые данные с веб-сайта, на котором мне нужно войти, чтобы увидеть фактический контент.Все работает нормально, но занимает около 5 секунд на запрос, что очень медленно для моих нужд (> 5000 URL для очистки).Кажется, есть более быстрые способы, такие как модули asyncio aiohttp.Однако все примеры, которые я нашел в Интернете, не показали, как войти на сайт и затем использовать эти инструменты.

Так что мне, в принципе, нужен простой, чтобы следовать примеру, как это сделать.

Я пытался перестроить этот пример: https://realpython.com/python-concurrency/#what-is-concurrency с моим кодом, который не работал.Я также попробовал AsyncHTMLSession () из request_html, который что-то возвратил, но, похоже, не запомнил логин.

Пока это мой код:

import requests
from bs4 import BeautifulSoup

payload = {
"name" : "username",
"password" : "example_pass",
"destination" : "MAS_Management_UserConsole",
"loginType" : ""
}

links = [several urls]

### stuff with requests
with requests.Session() as c:
    c.get('http://boldsystems.org/')
    c.post('http://boldsystems.org/index.php/Login', data = payload)

def return_id(link):
    page = c.get(link).content
    soup = BeautifulSoup(page, 'html.parser')
    return soup.find(id = 'processidLC').text

for link in links:
    print(return_id(link))

Ответы [ 2 ]

0 голосов
/ 17 мая 2019

Похоже, вы уже используете requests, поэтому вы можете попробовать request-async .Приведенный ниже пример должен помочь вам с частью « в разумные сроки » вашего вопроса, просто настройте функцию parse_html соответственно, чтобы найти ваш HTML-тег.По умолчанию он будет выполнять 50 запросов параллельно (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())

Вывод:

# 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

Теперь, это может все еще не помочь вам с вашей проблемой регистрации.HTML-тег, который вы ищете (или всю веб-страницу), может быть сгенерирован JavaScript, поэтому вам понадобятся такие инструменты, как requests-html, использующий браузер без заголовка для чтения содержимого, отображаемого с помощью JavaScript.

Также возможно, что ваша форма входа в систему использует защиту CSRF, например, при входе в бэкэнд администратора Django:

>>> 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

Мы используем сессию, чтобы сначала выполнить запрос get, чтобы получить токен из куки csrftoken изатем мы регистрируемся с двумя скрытыми полями формы:

<form action="/admin/login/?next=/admin/" method="post" id="login-form">
  <input type="hidden" name="csrfmiddlewaretoken" value="uqX4NIOkQRFkvQJ63oBr3oihhHwIEoCS9350fVRsQWyCrRub5llEqu1iMxIDWEem">
  <div class="form-row">
    <label class="required" for="id_username">Username:</label>
    <input type="text" name="username" autofocus="" required="" id="id_username">
  </div>
  <div class="form-row">
    <label class="required" for="id_password">Password:</label> <input type="password" name="password" required="" id="id_password">
    <input type="hidden" name="next" value="/admin/">
  </div>
    <div class="submit-row">
    <label>&nbsp;</label>
    <input type="submit" value="Log in">
  </div>
</form>

Примечание: примеры используют Python 3.7 +

0 голосов
/ 16 мая 2019

Посмотрите на asyncio и используйте функцию asyncio.gather.

Обернуть все, что находится ниже этой строки "links = [несколько URL-адресов]" в методе.

Будьте осторожны, это не потокобезопасно, поэтому не изменяйте переменные в методе.

Кроме того, это потоки, поэтому может быть полезно использовать asyncio.sleep (randint (0,2)), чтобы задержать некоторые потоки, чтобы не запускать все одновременно.

Затем, используя asyncio, вызовите приведенный ниже метод с новым URL-адресом, таким как

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

results = asyncio.gather(*tasks)

Надеюсь, это поможет.

В противном случае посмотрите на https://github.com/jreese/aiomultiprocess

...