Проблемы с python crawler при использовании aiohttp - PullRequest
0 голосов
/ 05 марта 2019

Я новичок в веб-пауках, и я так растерялся, когда использую aiohttp.Вот мой код:

header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1',
              'Referer': 'https://www.mzitu.com/',
               'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
               'Accept-Encoding': 'gzip',
     }

class MZiTu(object):
    def __init__(self):
        self.timeout = 5
        self.file_path = 'D:\mzitu'  
        self.common_page_url = 'https://www.mzitu.com/page/'
        self.total_page_num = 0
        self.end_album_num = 0
        self.session = None

    async def start(self):
        async with aiohttp.ClientSession(headers=header) as mzt.session:
            for page in range(1, self.total_page_num+1):
                await self.crawlAlbum(self.common_page_url, page)

    async def crawlAlbum(self, common_url, page_num):
        page_url = self.common_page_url + str(page_num)
        async with self.session.get(page_url, timeout=self.timeout) as resp:
            html = await resp.text()
            bsop = BeautifulSoup(html, 'lxml')
            album_items = bsop.find('ul', {'id': 'pins'}).findAll('li')
            for item in album_items:
                try:
                    album_title = item.find('img').attrs['alt']
                    album_url = item.find('a').attrs['href']
                    if not os.path.exists(os.path.join(self.file_path, album_title)):
                        os.mkdir(os.path.join(self.file_path, album_title))
                    os.chdir(os.path.join(self.file_path, album_title))
                    await self.crawlImgs(album_url)
                except:
                    continue

    async def crawlImgs(self, album_url):
        self.end_album_num = await self.getAlbumTotalNum(album_url)
        for i in range(1, self.end_album_num+1):
            img_page_url = album_url + str(i)
            async with self.session.get(img_page_url, timeout=self.timeout) as resq:
                html = await resq.text()
                bsop = BeautifulSoup(html, 'lxml')
                try:
                    img_url = bsop.find('div', {'class': 'main-image'}).find('img').attrs['src']
                    await self.downloadImg(i, img_url)
                except:
                    continue

    async def getAlbumTotalNum(self, album_url):
        async with self.session.get(album_url, timeout=self.timeout) as resq:
            html = await resq.text()
            bsop = BeautifulSoup(html, 'lxml')
            total_num = int(bsop.find('div', {'class': 'nav-links'}).findAll('a', {'class': 'page-numbers'})[-2].text)
            return total_num

    async def downloadImg(self,index, img_url):
        async with self.session.get(img_url, timeout=self.timeout) as resq:
            content = await resq.read()
            async with aiofiles.open(str(index)+'.jpg', 'wb') as f:
                await f.write(content)

if __name__ == "__main__":
    mzt = MZiTu()
    mzt.total_page_num = 2
    loop = asyncio.get_event_loop()
    to_do = [mzt.start()]
    wait_future = asyncio.wait(to_do)
    loop.run_until_complete(wait_future)
    loop.close()

мой код возвращается прямо в первой строке ниже, почему?так запутался

async def getAlbumTotalNum(self, album_url):
        async with self.session.get(album_url, timeout=self.timeout) as resq:
            html = await resq.text()
            bsop = BeautifulSoup(html, 'lxml')
            total_num = int(bsop.find('div', {'class': 'nav-links'}).findAll('a', {'class': 'page-numbers'})[-2].text)
            return total_num

Я не могу найти никаких ошибок в моей программе.так неловко.так неловко.если есть какие-то учебные материалы об aiohttp и asyncio, мне будет очень сложно.

1 Ответ

0 голосов
/ 05 марта 2019

Первая проблема заключается в том, что вы используете обработку исключений для покемонов , вы действительно не хотите перехватывать их все .

Поймать определенные исключения, только или, по крайней мере, только перехват Exception и убедитесь, что повторно подняли asyncio.CancelledError (вы не хотите блокировать отмены задач), а также зарегистрируйте или распечатайте возникшие исключения, чтобы вы могли выполнить дальнейшую очисткуваш обработчик.В качестве быстрого исправления я заменил ваши try:... except: continue блоки на:

try:
    # ...
except asyncio.CancelledError:
    raise
except Exception:
    traceback.print_exc()
    continue

и добавил import traceback вверху.Когда вы затем запускаете свой код, вы видите, почему ваш код дает сбой:

Traceback (most recent call last):
  File "test.py", line 43, in crawlAlbum
    await self.crawlImgs(album_url)
  File "test.py", line 51, in crawlImgs
    self.end_album_num = await self.getAlbumTotalNum(album_url)
  File "test.py", line 72, in getAlbumTotalNum
    total_num = int(bsop.find('div', {'class': 'nav-links'}).findAll('a', {'class': 'page-numbers'})[-2].text)
AttributeError: 'NoneType' object has no attribute 'findAll'

Либо изменился способ, которым сайт размечал ссылки, либо сайт использует Javascript для изменения DOM в браузере после загрузки HTML,В любом случае, использование общего предложения except: без регистрации ошибки скрывает такие проблемы от вас и затрудняет отладку.

Я бы, по крайней мере, добавил бы некоторые записи, чтобы записать, какой URL пытался коданализируйте, когда возникают исключения, поэтому вы можете повторить проблему в интерактивной, не асинхронной настройке и попробовать разные подходы для анализа страниц.

Вместо использования вызовов .find() и .findAll() используйте CSS селектор найти правильные элементы:

links = bsop.select(f'div.pagenavi a[href^="{album_url}"] span')
return 1 if len(links) < 3 else int(links[-2].string)

Вышеупомянутый URL использует текущий URL-адрес для ограничения поиска конкретными span элементами с родительским элементом a, которые имеют hrefатрибут, значение которого, по крайней мере, начинается с URL текущей страницы.

Обратите внимание, что вышеизложенное не единственная проблема, однако, когда эта проблема исправлена, следующим исключением является

Traceback (most recent call last):
  File "test.py", line 59, in crawlImgs
    img_url = bsop.find('div', {'class': 'main-image'}).find('img').attrs['src']
AttributeError: 'NoneType' object has no attribute 'find'

This oneна самом деле вызвано неправильной обработкой URL для альбомов, при условии, что они всегда заканчиваются /.Исправьте это:

async def crawlImgs(self, album_url):
    end_album_num = await self.getAlbumTotalNum(album_url)
    if album_url[-1] != '/':
        album_url += '/'
    for i in range(1, end_album_num + 1):
        img_page_url = album_url + str(i)
        # ...

Вы не хотите установить album_num в качестве атрибута для self однако!Состояние экземпляра класса является общим для задач, в то время как вы фактически не создаете несколько задач в своем коде (на данный момент это все одна последовательная задача), вы хотите избежать изменения общего состояния.

...