Python练习之使用XPath获取豆瓣电影排行榜
之前我们学习了XPath的简单使用,参考:https://www.cnblogs.com/minseo/p/15502584.html
今天我们来练习XPath的使用,使用XPath分析豆瓣电影排行榜,本次我们练习获取电影排行榜的新片榜信息,练习获取的内容是新片的url,影片名称,导演名
为了便于查看XPath分析html的整个过程我们把网页的源码下载下来,使用文本编辑器Notepad++打开,该文本编辑器可以清楚的查看标签的对应关系。
首先我们来查找10部新片的url
查看源码发现豆瓣新片榜包含在一个标签<div>内该标签有一个唯一识别的id=“conetent”
使用XPath查找这个标签
# 爬取豆瓣电影排行榜的新片帮,使用同步方法 # 导入模块 import requests from lxml import etree # 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} movice_url = 'https://movie.douban.com/chart' resp = requests.get(movice_url,headers=headers) # 获取响应的所有源码信息 movice_resp_text = resp.text # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) movice_url = movice_etree_element.xpath('//*[@id="content"]') print(movice_url)
XPath代码解析
xpath('//*[@id="content"]') // # 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 * # 通配符匹配所有 [@id="content"] # 匹配标签属性,本次这个id属性是唯一的所以可以匹配到一个唯一的标签
注意:匹配代码如果有多个引号则内部和外部不能使用相同的引号符号,即外部使用单引号则内部需要使用双引号,反之亦然
本次查找到一个对象输出如下
[<Element div at 0x134e93395c8>]
如果不确定是否查到的是这个标签,可以打印标签的关键属性查看
打印id属性可以看到确实是对应的标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/@id') print(movice_url) # ['content']
下面缩小范围继续查找,在上一步查找到的标签下继续查找<div>标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div') print(movice_url) # [<Element div at 0x1f2b3248688>]
又找到唯一一个,打印标签的class属性看一下找到的是哪个标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/@class') print(movice_url) # ['grid-16-8 clearfix']
可以看到找到的是源码里面对应的下面这个标签
很明显还没有找到对应的电影url
下面缩小范围继续查找
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div') print(movice_url) # [<Element div at 0x288c4f18508>, <Element div at 0x288c4f18548>, <Element div at 0x288c4f18488>]
在找到的上一个标签<div>下找到3个<div>标签,下面我们还是通过属性来确定新片在这三个标签中的哪一个标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div') print(movice_url[0].xpath('@class'),movice_url[1].xpath('@class'),movice_url[2].xpath('@class')) # ['article'] ['aside'] ['extra']
通过取列表的元素再使用xpath方法去查找属性class的值来确定3个标签的位置
关键关键字在源码中查找这三个标签的位置
很明显我们要找的电影url在第一个标签内
缩小范围继续查找,查找第一个标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]') print(movice_url) # print(movice_url)
[<Element div at 0x23daef97788>]
注意:这里第一个标签的下标是1而不是像python其他对象比如list的第一个标签是0
这次又返回一个对象
下面继续查找下一个div标签打印属性class
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/@class') print(movice_url) # ['indent']
又找到唯一一个<div>标签,在源码里如下位置
缩小范围继续查找
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div') print(movice_url) # [<Element div at 0x2d9b21186c8>]
又只有一个,新片还在这个标签范围内
缩小范围继续查,因为这个标签下面是table标签使用我们使用table标签查找
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table') print(movice_url) # [<Element table at 0x1bedccf8608>, <Element table at 0x1bedccf8588>, <Element table at 0x1bedccf85c8>, <Element table at 0x1bedccf8508>, <Element table at 0x1bedccf8188>, <Element table at 0x1bedccf8148>, <Element table at 0x1bedccf8108>, <Element table at 0x1bedccf8048>, <Element table at 0x1bedccf8088>, <Element table at 0x1bedccf8208>]
在这个<div>标签下包含了10个table标签,很明显新片10部电影的信息在这10个table内
缩小范围继续查找这10部电影对应的url下面查找tr标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr') print(movice_url) # [<Element tr at 0x2135c038648>, <Element tr at 0x2135c0385c8>, <Element tr at 0x2135c038608>, <Element tr at 0x2135c038548>, <Element tr at 0x2135c0381c8>, <Element tr at 0x2135c038188>, <Element tr at 0x2135c038148>, <Element tr at 0x2135c038088>, <Element tr at 0x2135c0380c8>, <Element tr at 0x2135c038248>]
还是10个对象,代表10部电影的内容都在这10行内
缩小范围继续查找
movice_etree_element = etree.HTML(movice_resp_text) movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td') print(movice_url)
这次返回了20个对象即20个td即每行有2列,我们取需要的列即第一列
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[1]') print(movice_url)
又返回10个列标签,我们需要是url信息就在这10个列内
是以下源码对应的列,一共10个只截取出一个展示
缩小范围继续查找这次我们找<a>标签
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[1]/a') print(movice_url)
没有问题还是返回10个对应的<a>标签,即源码对应的
下面去这个标签是href属性值就把10部电影的url取出来了
movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[1]/a/@href') print(movice_url) # ['https://movie.douban.com/subject/3001114/', 'https://movie.douban.com/subject/33457594/', 'https://movie.douban.com/subject/34820925/', 'https://movie.douban.com/subject/35235502/', 'https://movie.douban.com/subject/1428581/', 'https://movie.douban.com/subject/34626280/', 'https://movie.douban.com/subject/35158124/', 'https://movie.douban.com/subject/34874432/', 'https://movie.douban.com/subject/35115642/', 'https://movie.douban.com/subject/32568661/']
根据取得的url取其中一个url分析取得这部电影的电影名和导演名
原理和上述是一样的,关键是从源码中找出对应的信息
# 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} movice_url = 'https://movie.douban.com/subject/3001114/' resp = requests.get(movice_url,headers=headers) # 获取响应的所有源码信息 movice_resp_text = resp.text # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) movice_name = movice_etree_element.xpath('//*[@id="content"]/h1/span[1]/text()') movice_author = movice_etree_element.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') print(movice_name,movice_author) # ['沙丘 Dune'] ['丹尼斯·维伦纽瓦']
下面把以上代码修改成函数然后获取10部新片的信息,并查看执行的时间
d:/learn-python3/学习脚本/aiohttp/get_movice_use_sync.py
# 爬取豆瓣电影排行榜的新片帮,使用同步方法 # 导入模块 import requests from lxml import etree import time def get_movice_url(): # 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} movice_url = 'https://movie.douban.com/chart' resp = requests.get(movice_url,headers=headers) # 获取响应的所有源码信息 movice_resp_text = resp.text # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) # 获取10部电影的url返回一个list元素为10部电影url movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[1]/a/@href') return movice_url def get_movice_info(): movice_url = get_movice_url() # print(movice_url) # 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} # 定义空字典用于接收影片信息 movice_info = {} # 把获取到的电影url遍历出影片名称和导演 for url in movice_url: resp = requests.get(url,headers=headers) # 获取响应的所有源码信息 movice_resp_text = resp.text # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) # 获取影片的名称和导演 movice_name = movice_etree_element.xpath('//*[@id="content"]/h1/span[1]/text()') movice_author = movice_etree_element.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') # 把获取到的影片名称和导演作为一个value赋值给字典,字典key为影片url movice_info[url] = {'name':movice_name,'author':movice_author} return movice_info if __name__ == '__main__': start_time = time.time() movice_info = get_movice_info() print(movice_info) end_time = time.time() print(end_time-start_time)
输出如下,返回字典包含了影片的url,影片名称和导演名,执行时间为8秒多
{'https://movie.douban.com/subject/3001114/': {'name': ['沙丘 Dune'], 'author': ['丹尼斯·维伦纽瓦']}, 'https://movie.douban.com/subject/33457594/': {'name': ['摩加迪 沙 모가디슈'], 'author': ['柳昇完']}, 'https://movie.douban.com/subject/34820925/': {'name': ['钛 Titane'], 'author': ['朱利亚·迪库诺']}, 'https://movie.douban.com/subject/35235502/': {'name': ['驾驶我的车 ドライブ・マイ・カー'], 'author': ['滨口龙介']}, 'https://movie.douban.com/subject/1428581/': {'name': ['天书奇谭'], 'author': ['王树忱', '钱运达']}, 'https://movie.douban.com/subject/34626280/': {'name': ['月光光心慌慌:杀戮 Halloween Kills'], 'author': ['大卫·戈登·格林']}, 'https://movie.douban.com/subject/35158124/': {'name': ['盛夏未来'], 'author': ['陈正道']}, 'https://movie.douban.com/subject/34874432/': {'name': ['花束般的恋爱 花束みたいな恋をした '], 'author': ['土井裕泰']}, 'https://movie.douban.com/subject/35115642/': {'name': ['平行森林'], 'author': ['郑雷']}, 'https://movie.douban.com/subject/32568661/': {'name': ['妈妈的神奇小子 媽媽的神奇小子'], 'author': ['尹志文']}} 8.593812465667725
下面结合aiohttp和asyncio使用异步方法执行同样的操作
get_movice_use_async.py
# 爬取豆瓣电影排行榜的新片帮,使用异步方法 # 导入模块 import requests from lxml import etree import time import asyncio import aiohttp async def get_movice_url(): # 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} movice_url = 'https://movie.douban.com/chart' async with aiohttp.ClientSession() as session: async with session.get(movice_url,headers=headers) as resp: # print(resp.status) # 获取响应的所有源码信息 movice_resp_text = await resp.text() # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) # 获取10部电影的url返回一个list元素为10部电影url movice_url = movice_etree_element.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[1]/a/@href') return movice_url async def get_movice_info(url): # 设置访问客户端头部信息,不设置可能无法使用requests访问 headers = {'User-Agent': 'M'} # 把获取到的电影url遍历出影片名称和导演 async with aiohttp.ClientSession() as session: async with session.get(url,headers=headers) as resp: # 获取响应的所有源码信息 movice_resp_text = await resp.text() # 使用etree.HTML转换成可以使用xpath分析的lxml.etree._Element对象 movice_etree_element = etree.HTML(movice_resp_text) # 取影片的名称和导演 movice_name = movice_etree_element.xpath('//*[@id="content"]/h1/span[1]/text()') movice_author = movice_etree_element.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') # 把获取到的影片名称和导演作为一个value赋值给字典,字典key为影片url return {url:{'name':movice_name,'author':movice_author}} # 定义主执行协程函数main async def main(): # 首先调用协程函数获取影片url,返回一个list 元素为影片url movice_url = await get_movice_url() # 创建tasks把10部影片url作为参数传递给协程函数get_movice_info来获取影片名称和导演信息 tasks = [get_movice_info(url) for url in movice_url] # 使用asyncio.gather方法运行,10个请求是同时运行的,按顺序返回结果 movice_info = await asyncio.gather(*tasks) print(movice_info) # 执行并计算执行耗时 if __name__ == '__main__': start_time = time.time() asyncio.run(main()) end_time = time.time() print(end_time-start_time)
输出如下
[{'https://movie.douban.com/subject/3001114/': {'name': ['沙丘 Dune'], 'author': ['丹尼斯·维伦纽瓦']}}, {'https://movie.douban.com/subject/33457594/': {'name': ['摩加 迪沙 모가디슈'], 'author': ['柳昇完']}}, {'https://movie.douban.com/subject/34820925/': {'name': ['钛 Titane'], 'author': ['朱利亚·迪库诺']}}, {'https://movie.douban.com/subject/35235502/': {'name': ['驾驶我的车 ドライブ・マイ・カー'], 'author': ['滨口龙介']}}, {'https://movie.douban.com/subject/1428581/': {'name': ['天书奇谭'], 'author': ['王树忱', '钱运达']}}, {'https://movie.douban.com/subject/34626280/': {'name': ['月光光心慌慌:杀戮 Halloween Kills'], 'author': ['大卫·戈登·格林']}}, {'https://movie.douban.com/subject/35158124/': {'name': ['盛夏未来'], 'author': ['陈正道']}}, {'https://movie.douban.com/subject/34874432/': {'name': ['花束般的恋爱 花束み たいな恋をした'], 'author': ['土井裕泰']}}, {'https://movie.douban.com/subject/35115642/': {'name': ['平行森林'], 'author': ['郑雷']}}, {'https://movie.douban.com/subject/32568661/': {'name': ['妈妈的神奇小子 媽媽的神奇小子'], 'author': ['尹志文']}}] 1.7407007217407227
可以看到执行相同的任务异步耗时比同步耗时省很多