新手小白的一个python晋级爬虫实验,基于python的asyncio、aiohttp库采用异步协程方法抓去了豆瓣电影TOP250基本信息。
一、案例背景
新手小白在入门python爬虫的学习中,发现大部分前辈分享的抓取豆瓣TOP250电影信息的学习案例基本都是基于TOP250电影排行的主页信息(每页25条电影信息,共计10页)爬取电影名称、导演、演员、评分等基本信息,笔者在刚入门的时候采取该方法发现最终获取到的信息不太完整,比如缺乏编剧信息,演员、导演信息不完整等等,为了能获得更干净的数据,应该进入每条电影的具体网址链接里面,抓取上述信息。
故整体思路分为两步:1,从每个主页中获得25条电影的次级网站链接;2:进入每条电影的链接获得详细数据。
对每个主业使用循环分别获得25条电影链接,在对25条电影链接分别使用循环,会面临较多的I/O阻塞问题,对于每次循环网页获得信息后笔者均设置了5秒的休眠时间,单线程同步执行的情况下250个网页至少需要1250秒,即20分钟。试探性的使用了异步协程方法,惊奇的发现,最后跑出来的并写入csv文件的时间为173.63秒,3分钟不到。故分享一下自己的探索代码,看看能否有进一步提升效率的可行性,以待完善和提高。
二、核心代码
1.进入每部电影链接获取数据的代码
主要思路是构建一个异步函数,网上查询了一些方法,使用aiohttp库,最后写入csv数据部分还未使用异步方法改进,暂时还有
#构建异步协程函数,获取单页数据
async def get_data(href):
async with aiohttp.ClientSession() as session:
async with await session.get(href,headers=headers) as response2:
page_text= await response2.text()
html_data=etree.HTML(page_text)
div_info=html_data.xpath('/html/body/div[3]/div[1]/div[3]/div[1]/div[1]/div[1]/div[1]/div[2]')[0]
#获取片名
movie_name=html_data.xpath('/html/body/div[3]/div[1]/h1/span[1]/text()')[0]
#获取导演信息
director='/'.join(div_info.xpath('./span[1]/span[2]/a/text()')[0:])
#获取编剧信息
authors='/'.join(div_info.xpath('./span[2]/span[2]/a/text()')[0:])
#获取演员信息
actors='/'.join(div_info.xpath('./span[3]/span[2]//a/text()')[0:])
#电影类型
movie_type='/'.join(div_info.xpath('.//span[@property="v:genre"]/text()')[0:])
#制片国家/地区
country=div_info.xpath('.//span[contains(text(),"制片国家")]/following::text()[1]')[0].strip()
#语言
language=div_info.xpath('.//span[contains(text(),"语言")]/following::text()[1]')[0].strip()
#上映时间
year='/'.join(div_info.xpath('.//span[@property="v:initialReleaseDate"]/text()')[0:])
#片长/分钟
runtime=div_info.xpath('.//span[@property="v:runtime"]/text()')[0].replace('分钟','')
rate_data=html_data.xpath('/html/body/div[3]/div[1]/div[3]/div[1]/div[1]/div[1]/div[2]')[0]
#获取评分信息
rate_score=rate_data.xpath('./div[1]/div[2]/strong/text()')[0]
rate_amounts=rate_data.xpath('.//span[@property="v:votes"]/text()')[0]
csvwriter.writerow([movie_name,year,movie_type,runtime,country,
language,director,authors,actors,rate_score,rate_amounts])
await asyncio.sleep(5)
2.将获取电影数据的异步函数嵌入主函数
通过主函数获得各个电影的href,并将href参数传入上述异步函数,调用异步函数,并打包异步函数任务,建立异步函数待执行的Tasks任务集
async def get_movie_data(url):
async with aiohttp.ClientSession() as session:
async with await session.get(url,headers=headers) as response1:
domain_page_data = await response1.text()
domain_page=etree.HTML(domain_page_data)
lis=domain_page.xpath('/html/body/div[3]/div[1]/div/div[1]/ol/li')
tasks=[]
for li in lis:
name=li.xpath('./div/div[2]/div[1]/a/span[1]/text()')[0]
href=li.xpath('./div/div[2]/div[1]/a/@href')[0]
tasks.append(asyncio.create_task(get_data(href)))
await asyncio.wait(tasks)
await asyncio.sleep(5)
3.在main程序中设置循环语句,为以上函数传入10个电影主业的url,并执行以上的Tasks任务
if __name__ == '__main__':
t1=time.time()
for page in range(0,250,25):
url='https://movie.douban.com/top250?start={}&filter='.format(page)
headers={
#填入自己的headers信息即可
}
loop = asyncio.get_event_loop()
loop.run_until_complete(get_movie_data(url))
t2=time.time()
print(t2-t1)
总结
通过以上协程程序,跑完250个网页,并将提取到的数据写入csv文件共计173秒;
做为初步入门python爬虫之后的第一个晋级程序,初步试验结果如上,关于写入csv文件的环节应该还有效率提升的地方,还有待进一步完善