Python学习笔记-第20天:异步爬虫(2)

第二十天 异步爬虫(2)

今天计划用Python开发一套异步爬虫框架用来补充blog内容,学习项目及练习源码地址:
GitHub源码

在蜘蛛中提取想要的数据

lxml

通过lxml模块分析爬取到的页面数据,提取想要的内容。

封装一个selector类

from lxml import etree
from lxml.html import fromstring, tostring
from pyquery import PyQuery as pq

class Xpath(Selector):
    def parse_detail(self, html):
        d = etree.HTML(html)
        try:
            if self.attr is None:
                result = None
                if len(d.xpath(self.rule)) > 1:
                    result =  [entry for entry in d.xpath(self.rule)]
                else:
                    result = d.xpath(self.rule)

                if self.r_type == 'elstr' and len(result) > 0:
                    return [tostring(el,'utf-8').decode('utf-8') for el in result]
                elif self.r_type == 'text' and len(result) >0:
                    return [el.text for el in result]
                else:
                    return result

            return [entry.get(self.attr, None) for entry in d.xpath(self.rule)] if len(d.xpath(self.rule)) > 1 else \
                d.xpath(self.rule)[0]
        except IndexError:
            return None

要点:

  • lxml.etree 提供的xpath获取页面元素
  • tostring(el,'utf-8').decode('utf-8') 直接提取某元素内的html内容

在蜘蛛中使用:

 async def parser(self,data):
        try:
            # print('jianshu parser',data)
            title = Xpath('//section/h1/text()').parse_detail(data)[0]    
            content = Xpath('//*[@id="__next"]/div[1]/div/div/section[1]/article',r_type='elstr').parse_detail(data)[0]        
        except BaseException as e:
            self.logger.error(e)

自此,已经完成了蜘蛛对页面的爬取,以及数据的解析。

让蜘蛛定时爬取

在crawler函数中回调自身

async def crawler(self):
        await self.init_spiders()
        workers = [asyncio.Task(self.worker(), loop=self.loop)
                   for _ in range(self.max_tasks)]
        await self.q.join()

        for w in workers:
            w.cancel()
        await asyncio.sleep(5 * 60)
        await self.crawler() # 重新一直运行下去

注意:到目前为止,发现这样的方式有一点问题,不知道是否可以像nodejs一样可以尾回调,因为一直回调会导致嵌套过深的问题。

蜘蛛的启动

在启动中可以初始化一些辅助的服务,如mysql连击池等

def main(loop):
    print('starting kospider server')
    coros = [mysql.initpool(loop=loop), redis.init_pool(loop=loop)]
    loop.run_until_complete(asyncio.gather(*coros))
    
    engine = Engine(loop=loop)
    engine.run()

event_loop = asyncio.get_event_loop()
try:
    event_loop.run_until_complete(main(event_loop))
    print('stoped kospider server')
except BaseException as e:
    print('Main error',e)
finally:
    event_loop.close()    # 当轮训器关闭以后,所有没有执行完成的协成将全部关闭

明天正式将数据写入数据库,并在页面显示内容。

计划找一个能让蜘蛛定时执行的好方式。

你可能感兴趣的:(Python学习笔记-第20天:异步爬虫(2))