Мой скрипт встречается с ошибкой, когда он должен работать асинхронно - PullRequest
0 голосов
/ 12 декабря 2018

Я написал скрипт на python, использующий ассоциацию asyncio с библиотекой aiohttp для анализа имен во всплывающих окнах, запускаемых при нажатии кнопок контактной информации, из информации другого агентства, расположенной в таблице из этот сайт асинхронно.Веб-страница отображает табличное содержимое на 513 страницах.

Я столкнулся с этой ошибкой too many file descriptors in select(), когда попытался с asyncio.get_event_loop(), но когда я наткнулся на эту тему , я мог видеть, что естьпредложение использовать asyncio.ProactorEventLoop(), чтобы избежать такой ошибки, поэтому я использовал последнее, но заметил, что, даже когда я выполнил это предложение, скрипт собирает имена только с нескольких страниц, пока не выдаст следующую ошибку.Как я могу это исправить?

raise client_error(req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host www.tursab.org.tr:443 ssl:None [The semaphore timeout period has expired]

Это моя попытка:

import asyncio
import aiohttp
from bs4 import BeautifulSoup

links = ["https://www.tursab.org.tr/en/travel-agencies/search-travel-agency?sayfa={}".format(page) for page in range(1,514)]
lead_link = "https://www.tursab.org.tr/en/displayAcenta?AID={}"

async def get_links(url):
    async with asyncio.Semaphore(10):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                text = await response.text()
                result = await process_docs(text)
            return result

async def process_docs(html):
    coros = []
    soup = BeautifulSoup(html,"lxml")
    items = [itemnum.get("data-id") for itemnum in soup.select("#acentaTbl tr[data-id]")]
    for item in items:
        coros.append(fetch_again(lead_link.format(item)))
    await asyncio.gather(*coros)

async def fetch_again(link):
    async with asyncio.Semaphore(10):
        async with aiohttp.ClientSession() as session:
            async with session.get(link) as response:
                text = await response.text()
                sauce = BeautifulSoup(text,"lxml")
                try:
                    name = sauce.select_one("p > b").text
                except Exception: name = ""
                print(name)

if __name__ == '__main__':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(asyncio.gather(*(get_links(link) for link in links)))

Короче говоря, функция process_docs() собирает data-id чисел с каждогостраницы, чтобы использовать их в качестве префикса этой ссылки https://www.tursab.org.tr/en/displayAcenta?AID={} для сбора имен из всплывающих окон.Одним из таких идентификаторов является 8757, и, следовательно, одна из таких квалифицированных ссылок https://www.tursab.org.tr/en/displayAcenta?AID=8757.

Кстати, если я изменю наибольшее число, используемое в переменной links, на 20 или 30 или около того, все пройдет гладко.

1 Ответ

0 голосов
/ 12 декабря 2018
async def get_links(url):
    async with asyncio.Semaphore(10):

Вы не можете сделать такую ​​вещь: это означает, что при каждом вызове функции будет создаваться новый экземпляр семафора, в то время как вам нужно будет создать один экземпляр семафора для всех запросов.Измените ваш код следующим образом:

sem = asyncio.Semaphore(10)  # module level

async def get_links(url):
    async with sem:
        # ...


async def fetch_again(link):
    async with sem:
        # ...

Вы также можете вернуть цикл по умолчанию, как только вы правильно используете семафор:

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(...)

Наконец, вы должны изменить и get_links(url), и * 1009.* выполнить синтаксический анализ вне семафора, чтобы освободить его как можно скорее, до того, как внутри семафора потребуется process_docs(text).

Окончательный код:

import asyncio
import aiohttp
from bs4 import BeautifulSoup

links = ["https://www.tursab.org.tr/en/travel-agencies/search-travel-agency?sayfa={}".format(page) for page in range(1,514)]
lead_link = "https://www.tursab.org.tr/en/displayAcenta?AID={}"

sem = asyncio.Semaphore(10)

async def get_links(url):
    async with sem:
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                text = await response.text()
    result = await process_docs(text)
    return result

async def process_docs(html):
    coros = []
    soup = BeautifulSoup(html,"lxml")
    items = [itemnum.get("data-id") for itemnum in soup.select("#acentaTbl tr[data-id]")]
    for item in items:
        coros.append(fetch_again(lead_link.format(item)))
    await asyncio.gather(*coros)

async def fetch_again(link):
    async with sem:
        async with aiohttp.ClientSession() as session:
            async with session.get(link) as response:
                text = await response.text()
    sauce = BeautifulSoup(text,"lxml")
    try:
        name = sauce.select_one("p > b").text
    except Exception:
        name = "o"
    print(name)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.gather(*(get_links(link) for link in links)))
...