大师兄的Python学习笔记(二十一): 爬虫(二)

大师兄的Python学习笔记(二十): 爬虫(一)
大师兄的Python学习笔记(二十二): 爬虫(三)

三、提取信息

2. 使用XPATH
  • 大师兄的Python学习笔记(十九): Python与(XML和JSON)
  • 相比正则表达式,用选择器从网页定位资源更精准。
>>>import requests
>>>from lxml import etree

>>>def sort_data(func):
>>>    def deco(*args,**kargs):
>>>        # 处理内容
>>>        data = func(*args,**kargs)
>>>        html_data = etree.HTML(data) # 补全html文件
>>>        index = html_data.xpath('//div[@class="pic"]/em[@class=""]//text()')
>>>        link = html_data.xpath('//div[@class="hd"]/a/@href')
>>>        name = html_data.xpath('//div[@class="hd"]/a/span[@class="title"][1]/text()')
>>>        director_and_actor = [(x.strip()).split('\xa0\xa0\xa0') for x in html_data.xpath('//div[@class="bd"]/p[1]/text()[1]')]
>>>        director = [x[0] for x in director_and_actor]
>>>        actor = [x[1] for x in director_and_actor]
>>>        inq = html_data.xpath('//div[@class="bd"]/div[@class="star"]/span[4]/text()')
>>>
>>>        result = zip(index,name,director,actor,inq,link)
>>>        return result
>>>    return deco

>>>@sort_data
>>>def get_page(url):
>>>    # 获得页面内容
>>>    headers = {
>>>        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like >>>>Gecko) Chrome/78.0.3904.108 Safari/537.36'
>>>    }
>>>    res = requests.get(url=url,headers=headers)
>>>    if res.status_code == 200:
>>>        return res.text # 获得HTML页面
>>>    else:
>>>        return None

>>>def show_result(data):
>>>    # 打印结果
>>>    for i in range(10):
>>>        print(next(data))

>>>def main():
>>>    # 入口
>>>    url = 'https://movie.douban.com/top250'
>>>    page_data = get_page(url)
>>>    show_result(page_data)

>>>if __name__ == '__main__':
>>>    main()
('1', '肖申克的救赎', '导演: 弗兰克·德拉邦特 Frank Darabont', '主演: 蒂姆·罗宾斯 Tim Robbins /...', '2077511人评价', 'https://movie.douban.com/subject/1292052/')
('2', '霸王别姬', '导演: 陈凯歌 Kaige Chen', '主演: 张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...', '1540213人评价', 'https://movie.douban.com/subject/1291546/')
('3', '阿甘正传', '导演: 罗伯特·泽米吉斯 Robert Zemeckis', '主演: 汤姆·汉克斯 Tom Hanks / ...', '1569821人评价', 'https://movie.douban.com/subject/1292720/')
('4', '这个杀手不太冷', '导演: 吕克·贝松 Luc Besson', '主演: 让·雷诺 Jean Reno / 娜塔莉·波特曼 ...', '1760375人评价', 'https://movie.douban.com/subject/1295644/')
('5', '美丽人生', '导演: 罗伯托·贝尼尼 Roberto Benigni', '主演: 罗伯托·贝尼尼 Roberto Beni...', '983764人评价', 'https://movie.douban.com/subject/1292063/')
('6', '泰坦尼克号', '导演: 詹姆斯·卡梅隆 James Cameron', '主演: 莱昂纳多·迪卡普里奥 Leonardo...', '1522727人评价', 'https://movie.douban.com/subject/1292722/')
('7', '千与千寻', '导演: 宫崎骏 Hayao Miyazaki', '主演: 柊瑠美 Rumi Hîragi / 入野自由 Miy...', '1631425人评价', 'https://movie.douban.com/subject/1291561/')
('8', '辛德勒的名单', '导演: 史蒂文·斯皮尔伯格 Steven Spielberg', '主演: 连姆·尼森 Liam Neeson...', '799685人评价', 'https://movie.douban.com/subject/1295124/')
('9', '盗梦空间', '导演: 克里斯托弗·诺兰 Christopher Nolan', '主演: 莱昂纳多·迪卡普里奥 Le...', '1499201人评价', 'https://movie.douban.com/subject/3541415/')
('10', '忠犬八公的故事', '导演: 莱塞·霍尔斯道姆 Lasse Hallström', '主演: 理查·基尔 Richard Ger...', '1043142人评价', 'https://movie.douban.com/subject/3011091/')
3. 使用Beautiful Soup
3.1 关于bs4
  • Beautiful Soup是一个的网页解析库,可以方便的从网页中提取数据。
  • 由于我们目前通常使用的是Beautiful Soup第四版,所以安装时使用:pip install beautifulsoup4 / conda install beautifulsoup4
  • 引用时使用:from bs4 import BeautifulSoup
>>>from bs4 import BeautifulSoup
>>>s = BeautifulSoup('Hello World!','html.parser')
>>>print(s.view.string)
Hello World!
3.2 解析器
  • bs4支持多种解析器:
解析器 优点 缺点
html.parser Python标准库;
速度适中;
容错能力强
老版本Python容错能力差
lxml 速度快;
容错能力强;
需要安装第三方包
xml 速度快;
唯一支持xml;
需要安装第三方包
html5lib 最强容错;
生成HTML5文档
速度慢
3.3 创建BeautifulSoup对象
  • 使用bs4.BeautifulSoup()函数,将一段html字符串解析成BeautifulSoup对象。
>>>html = '''
>>>            
>>>                
>>>                    wemud
>>>                
>>>            
>>>            
>>>                

>>> wemud >>>

>>>

>>> play free online. >>>

>>> >>> click to join >>> >>> >>> ''' >>>s = BeautifulSoup(html,'html.parser') >>>print(type(s))
3.4 内容选择
  • 有了BeautifulSoup对象后,可以使用非常"Python"的方法选择内容。
3.4.1 节点选择器

1) 选择节点元素

  • 通过调用节点名称就可以选择节点元素
  • 默认第一个元素。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.p)

play free online.

2) 使用.name属性获得节点名称

  • 默认第一个元素。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.p.name)
p

3) 使用.attrs获得节点属性

  • 返回的是一个属性dict。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.p.attrs)
{'class': ['title']}

4) 使用.string获得文本内容

>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.title.string)
wemud

5) 嵌套选择

  • 由于每个节点都是bs4.element.Tag对象,所以可以嵌套选择。
  • 默认第一个元素。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.body.p.string)
wemud

6) 关联选择

  • 使用.contents获得直接子节点元素列表:
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.body.contents)
['\n', 

wemud

, '\n',

play free online.

, '\n', click to join , '\n']
  • 使用.children获得直接子节点元素的列表生成器:
>>>s = BeautifulSoup(html,'html.parser')
>>>for child in s.body.children:
>>>    print(child)

wemud

play free online.

click to join
  • 使用.descendants获得所有子节点元素的生成器:
>>>s = BeautifulSoup(html,'html.parser')
>>>for index,child in enumerate(s.html.descendants):
>>>    print(f'{index}:{child}')
0:

1:

                   wemud
               

2:

3:
                   wemud
               
4:
                   wemud
               
5:

6:

7:

wemud

play free online.

click to join 8: 9:

wemud

10: wemud 11: 12:

play free online.

13: play free online. 14: 15: click to join 16: click to join 17: 18:
  • 使用.parent获得父节点元素:
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.p.parent.name)
body
  • 使用.parents获得所有祖先节点元素的生成器:
>>>s = BeautifulSoup(html,'html.parser')
>>>parents = s.title.parents
>>>while True:
>>>    try:
>>>        print(next(parents).name)
>>>    except StopIteration as e:
>>>        break
head
html
[document]
  • 使用.next_sibling.previous_sibling获得兄弟节点元素:
>>>s = BeautifulSoup(html,'html.parser')
>>>>print((s.head.next_sibling).name)
body
>>>print((s.body.previous_sibling).name)
head
3.4.2 方法选择器
  • 方法选择器可以根据条件定位元素。
  • 查询方法:

1) find(name,attrs,recursive,text,**kwargs)和find_all(name,attrs,recursive,text,**kwargs)

  • 常用方法。
  • 它们的区别是查找第一个元素或全部元素。

2) find_parent()和find_parents()

  • 区别为查询父节点和所有祖先节点。

3) find_next_sibling()和find_next_siblings()

  • 区别为查询下一个或所有兄弟节点。

4) find_previous_sibling()和find_previous_siblings()

  • 区别为查询上一个或所有兄弟节点。

5) find_next()和find_all_next()

  • 区别为查询节点后第一个或所有符合条件的节点。

6) find_previous()和find_all_previous()

  • 区别为查询节点前第一个或所有符合条件的节点。
  • 查询参数:

1) 节点名查询

  • 使用name参数查询节点名。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(name="p"))
[

wemud

,

play free online.

]

2) 节点属性查询

  • 使用attrs参数传入属性-值的字典进行查询。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(attrs={"class":"title"}))
[

wemud

]
  • 常用属性可以直接作为参数进行查询。
  • 如果属性名正好是Python关键字,则在后面加_,比如:class_
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(class_="title"))
[

wemud

]

3) 节点文本查询

  • 使用text参数查询文本内容。
  • 可以传入字符串或正则表达式。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(text=re.compile("wemud")))
['\n                    wemud\n                ', '\n                    wemud\n                ']
3.4.3 CSS选择器
  • CSS选择器是Web开发常用的工具。
  • 使用select(selector, namespaces=None, limit=None, **kwargs)方法传入CSS选择器即可。
>>>s = BeautifulSoup(html,'html.parser')
>>>for title in s.select(".title"):
>>>    print(title.get_text())

>>>for link1 in s.select("#link1"):
>>>    print(link1.attrs)
                    wemud
                
{'id': 'link1', 'href': 'www.wemud.net.cn'}
3.5 使用bs4实现demo
>>>import requests,re
>>>from bs4 import BeautifulSoup

>>>def sort_data(func):
>>>    def deco(*args,**kargs):
>>>        # 处理内容
>>>        data = func(*args,**kargs)
>>>        html_data = BeautifulSoup(data)
>>>        index = [x.string for x in html_data.find_all(name="em")]
>>>        name = [x.find(class_="title").string for x in html_data.find_all(class_="hd")]
>>>        director_actor = [x.strip() for x in html_data.find_all(text=re.compile("导演:(.*?)"))]
>>>        director = [x.split('\xa0\xa0\xa0')[0] for x in director_actor]
>>>        actor = [x.split('\xa0\xa0\xa0')[1] for x in director_actor]
>>>        star = [x for x in html_data.find_all(text=re.compile('.*?人评价'))]
>>>        link = [x.a.attrs['href'] for x in html_data.find_all(class_="hd")]

>>>        result = zip(index,name,director,actor,star,link)
>>>        return result
>>>    return deco

>>>@sort_data
>>>def get_page(url):
>>>    # 获得页面内容
>>>    headers = {
>>>        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like >>>>Gecko) Chrome/78.0.3904.108 Safari/537.36'
>>>    }
>>>    res = requests.get(url=url,headers=headers)
>>>    if res.status_code == 200:
>>>        return res.text # 获得HTML页面
>>>    else:
>>>        return None

>>>def show_result(data):
>>>    # 打印结果
>>>    for i in range(10):
>>>        print(next(data))

>>>def main():
>>>    # 入口
>>>    url = 'https://movie.douban.com/top250'
>>>    page_data = get_page(url)
>>>    show_result(page_data)

>>>if __name__ == '__main__':
>>>    main()
('1', '肖申克的救赎', '导演: 弗兰克·德拉邦特 Frank Darabont', '主演: 蒂姆·罗宾斯 Tim Robbins /...', '2077511人评价', 'https://movie.douban.com/subject/1292052/')
('2', '霸王别姬', '导演: 陈凯歌 Kaige Chen', '主演: 张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...', '1540213人评价', 'https://movie.douban.com/subject/1291546/')
('3', '阿甘正传', '导演: 罗伯特·泽米吉斯 Robert Zemeckis', '主演: 汤姆·汉克斯 Tom Hanks / ...', '1569821人评价', 'https://movie.douban.com/subject/1292720/')
('4', '这个杀手不太冷', '导演: 吕克·贝松 Luc Besson', '主演: 让·雷诺 Jean Reno / 娜塔莉·波特曼 ...', '1760375人评价', 'https://movie.douban.com/subject/1295644/')
('5', '美丽人生', '导演: 罗伯托·贝尼尼 Roberto Benigni', '主演: 罗伯托·贝尼尼 Roberto Beni...', '983764人评价', 'https://movie.douban.com/subject/1292063/')
('6', '泰坦尼克号', '导演: 詹姆斯·卡梅隆 James Cameron', '主演: 莱昂纳多·迪卡普里奥 Leonardo...', '1522727人评价', 'https://movie.douban.com/subject/1292722/')
('7', '千与千寻', '导演: 宫崎骏 Hayao Miyazaki', '主演: 柊瑠美 Rumi Hîragi / 入野自由 Miy...', '1631425人评价', 'https://movie.douban.com/subject/1291561/')
('8', '辛德勒的名单', '导演: 史蒂文·斯皮尔伯格 Steven Spielberg', '主演: 连姆·尼森 Liam Neeson...', '799685人评价', 'https://movie.douban.com/subject/1295124/')
('9', '盗梦空间', '导演: 克里斯托弗·诺兰 Christopher Nolan', '主演: 莱昂纳多·迪卡普里奥 Le...', '1499201人评价', 'https://movie.douban.com/subject/3541415/')
('10', '忠犬八公的故事', '导演: 莱塞·霍尔斯道姆 Lasse Hallström', '主演: 理查·基尔 Richard Ger...', '1043142人评价', 'https://movie.douban.com/subject/3011091/')
4. 使用Pyquery
  • Pyquery是Jquery的"Python版",语法和Jquery类似。
  • 相比Beautiful Soup更灵活。
  • 通常使用from pyquery import PyQuery as pq导入
4.1 初始化pyquery对象
  • 与bs4类似,解析前需要先初始化对象。
  • 使用PyQuery()方法初始化字符串对象。
>>>from pyquery import PyQuery as pq

>>>html = '''
>>>            
>>>                
>>>                    wemud
>>>                
>>>            
>>>            
>>>                

>>> wemud >>>

>>>

>>> play free online. >>>

>>> >>> click to join >>> >>> >>> ''' >>>page = pq(html) # 初始化对象 >>>print(type(page))
  • 也可以直接传入网页URL进行初始化对象。
>>>from pyquery import PyQuery as pq
>>>page = pq(url="http://www.baidu.com")
>>>print(type(page))

  • 也可以直接从文件初始化。
>>>from pyquery import PyQuery as pq
page = pq(filename="test.xml")
print(type(page))

4.2 CSS选择器
4.2.1 基本用法
  • 与前端的CSS选择器语法类似。
>>>page = pq(html)
>>>print(page('body .title'))

wemud

>>>print(page('#link1')) click to join
4.2.2 查询方法

1) find()函数

  • 查询所有符合条件的子节点。
>>>page = pq(html)
>>>body = page("body")
>>>print(body.find('p'))

wemud

play free online.

2) parent()函数

  • 获得目标的父节点。
>>>page = pq(html)
>>>title = page("title")
>>>print(title.parent())

               
                   wemud
               
           

3) parents()函数

  • 获得所有符合条件的祖先节点。
>>>page = pq(html)
>>>title = page("title")
>>>print(title.parents('head'))

               
                   wemud
               
           

4) siblings()函数

  • 获得所有符合条件的兄弟节点。
>>>page = pq(html)
>>>a = page("#link1")
>>>print(a.siblings('.title'))

wemud

5) items()函数

  • 用于获取返回结果的生成器。
>>>page = pq(html)
>>>p = page("p")
>>>print(type(p))

>>>print(type(p.items()))

4.2.3 获取内容

1) 获取属性

  • 使用attr()函数,或.attr属性。
>>>page = pq(html)
>>>a = page("#link1")
>>>print(a.attr('href'))
www.wemud.net.cn
>>>print(a.attr.href)
www.wemud.net.cn

2) 获取文本

  • 使用text()函数获得目标的纯文本信息。
  • 使用html()函数获得目标的html文本信息。
>>>page = pq(html)
>>>body = page("body")
>>>print(body.text())
wemud
play free online.
click to join
>>>print(body.html())
               

wemud

play free online.

click to join
4.2.4 节点动态操作
  • pyquery支持对节点进行动态修改,常用方法如下:

1) 变更class属性

  • 使用addClass()方法增加。
>>>page = pq(html)
>>>body = page("body")
>>>body.addClass("body")
>>>print(body.attr.class_)
body
  • 使用removeClass()方法删除。
>>>page = pq(html)
>>>p = page(".title")
>>>p.removeClass("title")
>>>print(p)

wemud

2) 变更节点属性

  • 使用attr()方法修改属性。
>>>page = pq(html)
>>>p = page(".content")
>>>p.attr("class","new class")
>>>p.attr("id","new id")
>>>print(p)

play free online.

  • 使用text()方法修改纯文本。
>>>page = pq(html)
>>>p = page(".content")
>>>p.text("some new content")
>>>print(p)

some new content

  • 使用html()方法修改html文本。
>>>page = pq(html)
>>>p = page(".content")
>>>p.html("some new content")
>>>print(p)

some new content

3) 变更节点

  • 使用append()方法增加节点。
>>>page = pq(html)
>>>body = page("body")
>>>body.append("

i'm new here

") >>>print(body)

wemud

play free online.

click to join

i'm new here

  • 使用remove()方法删除节点。
>>>page = pq(html)
>>>p = page(".content")
>>>p.remove()
>>>print(page("body"))

               

wemud

click to join
4.2.5 伪类选择器
  • pyquery支持各种伪类选择器。
  • 全部伪类选择器
>>>page = pq(html)
>>>print(page('.title:first-child'))

wemud

4.3 使用pyquery实现demo
>>>from pyquery import PyQuery as pq
>>>import requests

>>>def sort_data(func):
>>>    def deco(*args,**kargs):
>>>        # 处理内容
>>>        data = func(*args,**kargs)
>>>        html_data = pq(data)
>>>        hd = html_data('.hd')
>>>        bd = html_data('.bd')

>>>        index = [x.text() for x in html_data.find('em').items()]
>>>        name = [x.text() for x in hd.find('.title:first-child').items()]
>>>        director_actor = [x.html().strip().split('
')[0] for x in bd.children('.star').siblings('p:first-of-type').items()] >>> director = [x.split('\xa0\xa0\xa0')[0] for x in director_actor] >>> actor = [x.split('\xa0\xa0\xa0')[1] for x in director_actor] >>> star = bd('.star')('span:nth-child(4)').text().split() >>> link = [x.attr.href for x in hd('a').items()] >>> result = zip(index,name,director,actor,star,link) >>> return result >>> return deco >>>@sort_data >>>def get_page(url): >>> # 获得页面内容 >>> headers = { >>> 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like >>>>Gecko) Chrome/78.0.3904.108 Safari/537.36' >>> } >>> res = requests.get(url=url,headers=headers) >>> if res.status_code == 200: >>> return res.text # 获得HTML页面 >>> else: >>> return None >>>def show_result(data): >>> # 打印结果 >>> for i in range(10): >>> print(next(data)) >>>def main(): >>> # 入口 >>> url = 'https://movie.douban.com/top250' >>> page_data = get_page(url) >>> show_result(page_data) >>>if __name__ == '__main__': >>> main() ('1', '肖申克的救赎', '导演: 弗兰克·德拉邦特 Frank Darabont', '主演: 蒂姆·罗宾斯 Tim Robbins /...', '2077511人评价', 'https://movie.douban.com/subject/1292052/') ('2', '霸王别姬', '导演: 陈凯歌 Kaige Chen', '主演: 张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...', '1540213人评价', 'https://movie.douban.com/subject/1291546/') ('3', '阿甘正传', '导演: 罗伯特·泽米吉斯 Robert Zemeckis', '主演: 汤姆·汉克斯 Tom Hanks / ...', '1569821人评价', 'https://movie.douban.com/subject/1292720/') ('4', '这个杀手不太冷', '导演: 吕克·贝松 Luc Besson', '主演: 让·雷诺 Jean Reno / 娜塔莉·波特曼 ...', '1760375人评价', 'https://movie.douban.com/subject/1295644/') ('5', '美丽人生', '导演: 罗伯托·贝尼尼 Roberto Benigni', '主演: 罗伯托·贝尼尼 Roberto Beni...', '983764人评价', 'https://movie.douban.com/subject/1292063/') ('6', '泰坦尼克号', '导演: 詹姆斯·卡梅隆 James Cameron', '主演: 莱昂纳多·迪卡普里奥 Leonardo...', '1522727人评价', 'https://movie.douban.com/subject/1292722/') ('7', '千与千寻', '导演: 宫崎骏 Hayao Miyazaki', '主演: 柊瑠美 Rumi Hîragi / 入野自由 Miy...', '1631425人评价', 'https://movie.douban.com/subject/1291561/') ('8', '辛德勒的名单', '导演: 史蒂文·斯皮尔伯格 Steven Spielberg', '主演: 连姆·尼森 Liam Neeson...', '799685人评价', 'https://movie.douban.com/subject/1295124/') ('9', '盗梦空间', '导演: 克里斯托弗·诺兰 Christopher Nolan', '主演: 莱昂纳多·迪卡普里奥 Le...', '1499201人评价', 'https://movie.douban.com/subject/3541415/') ('10', '忠犬八公的故事', '导演: 莱塞·霍尔斯道姆 Lasse Hallström', '主演: 理查·基尔 Richard Ger...', '1043142人评价', 'https://movie.douban.com/subject/3011091/')

参考资料


  • https://blog.csdn.net/u010138758/article/details/80152151 J-Ombudsman
  • https://www.cnblogs.com/zhuluqing/p/8832205.html moisiet
  • https://www.runoob.com 菜鸟教程
  • http://www.tulingxueyuan.com/ 北京图灵学院
  • http://www.imooc.com/article/19184?block_id=tuijian_wz#child_5_1 两点水
  • https://blog.csdn.net/weixin_44213550/article/details/91346411 python老菜鸟
  • https://realpython.com/python-string-formatting/ Dan Bader
  • https://www.liaoxuefeng.com/ 廖雪峰
  • https://blog.csdn.net/Gnewocean/article/details/85319590 新海说
  • https://www.cnblogs.com/Nicholas0707/p/9021672.html Nicholas
  • https://www.cnblogs.com/dalaoban/p/9331113.html 超天大圣
  • https://blog.csdn.net/zhubao124/article/details/81662775 zhubao124
  • https://blog.csdn.net/z59d8m6e40/article/details/72871485 z59d8m6e40
  • 《Python学习手册》Mark Lutz
  • 《Python编程 从入门到实践》Eric Matthes

本文作者:大师兄(superkmi)

你可能感兴趣的:(大师兄的Python学习笔记(二十一): 爬虫(二))