第二十天 異步爬蟲(chóng)(2)
今天計(jì)劃用Python開(kāi)發(fā)一套異步爬蟲(chóng)框架用來(lái)補(bǔ)充blog內(nèi)容,學(xué)習(xí)項(xiàng)目及練習(xí)源碼地址:
GitHub源碼
在蜘蛛中提取想要的數(shù)據(jù)
lxml
通過(guò)lxml模塊分析爬取到的頁(yè)面數(shù)據(jù),提取想要的內(nèi)容。
封裝一個(gè)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
要點(diǎn):
- lxml.etree 提供的xpath獲取頁(yè)面元素
-
tostring(el,'utf-8').decode('utf-8')直接提取某元素內(nèi)的html內(nèi)容
在蜘蛛中使用:
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)
自此,已經(jīng)完成了蜘蛛對(duì)頁(yè)面的爬取,以及數(shù)據(jù)的解析。
讓蜘蛛定時(shí)爬取
在crawler函數(shù)中回調(diào)自身
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() # 重新一直運(yùn)行下去
注意:到目前為止,發(fā)現(xiàn)這樣的方式有一點(diǎn)問(wèn)題,不知道是否可以像nodejs一樣可以尾回調(diào),因?yàn)橐恢被卣{(diào)會(huì)導(dǎo)致嵌套過(guò)深的問(wèn)題。
蜘蛛的啟動(dòng)
在啟動(dòng)中可以初始化一些輔助的服務(wù),如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() # 當(dāng)輪訓(xùn)器關(guān)閉以后,所有沒(méi)有執(zhí)行完成的協(xié)成將全部關(guān)閉
明天正式將數(shù)據(jù)寫入數(shù)據(jù)庫(kù),并在頁(yè)面顯示內(nèi)容。
計(jì)劃找一個(gè)能讓蜘蛛定時(shí)執(zhí)行的好方式。