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

2020-07-18  本文已影响0人  superkmi

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

三、提取信息

2. 使用XPATH
>>>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
>>>from bs4 import BeautifulSoup
>>>s = BeautifulSoup('<view>Hello World!</view>','html.parser')
>>>print(s.view.string)
Hello World!
3.2 解析器
解析器 优点 缺点
html.parser Python标准库;
速度适中;
容错能力强
老版本Python容错能力差
lxml 速度快;
容错能力强;
需要安装第三方包
xml 速度快;
唯一支持xml;
需要安装第三方包
html5lib 最强容错;
生成HTML5文档
速度慢
3.3 创建BeautifulSoup对象
>>>html = '''<html>
>>>            <head>
>>>                <title>
>>>                    wemud
>>>                </title>
>>>            </head>
>>>            <body>
>>>                <p class="title">
>>>                    wemud
>>>                </p>
>>>                <p class="content">
>>>                    play free online.
>>>                </p>
>>>                <a id="link1" href="www.wemud.net.cn">
>>>                    click to join
>>>                </a>
>>>            </body>
>>>        </html>'''
>>>s = BeautifulSoup(html,'html.parser')
>>>print(type(s))
<class 'bs4.BeautifulSoup'>
3.4 内容选择
3.4.1 节点选择器

1) 选择节点元素

  • 通过调用节点名称就可以选择节点元素
  • 默认第一个元素。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.p)
<p class="title&gt; wemud &lt;/p&gt; &lt;p class=" content"="">
                   play free online.
               </p>

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', <p class="title">
                   wemud
               </p>, '\n', <p class="content">
                   play free online.
               </p>, '\n', <a href="www.wemud.net.cn" id="link1">
                   click to join
               </a>, '\n']
  • 使用.children获得直接子节点元素的列表生成器:
>>>s = BeautifulSoup(html,'html.parser')
>>>for child in s.body.children:
>>>    print(child)
<p class="title">
                   wemud
               </p>

<p class="content">
                   play free online.
               </p>

<a href="www.wemud.net.cn" id="link1">
                   click to join
               </a>
  • 使用.descendants获得所有子节点元素的生成器:
>>>s = BeautifulSoup(html,'html.parser')
>>>for index,child in enumerate(s.html.descendants):
>>>    print(f'{index}:{child}')
0:

1:<head>
<title>
                   wemud
               </title>
</head>
2:

3:<title>
                   wemud
               </title>
4:
                   wemud
               
5:

6:

7:<body>
<p class="title">
                   wemud
               </p>
<p class="content">
                   play free online.
               </p>
<a href="www.wemud.net.cn" id="link1">
                   click to join
               </a>
</body>
8:

9:<p class="title">
                   wemud
               </p>
10:
                   wemud
               
11:

12:<p class="content">
                   play free online.
               </p>
13:
                   play free online.
               
14:

15:<a href="www.wemud.net.cn" id="link1">
                   click to join
               </a>
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"))
[<p class="title">
                   wemud
               </p>, <p class="content">
                   play free online.
               </p>]

2) 节点属性查询

  • 使用attrs参数传入属性-值的字典进行查询。
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(attrs={"class":"title"}))
[<p class="title">
                   wemud
               </p>]
  • 常用属性可以直接作为参数进行查询。
  • 如果属性名正好是Python关键字,则在后面加_,比如:class_
>>>s = BeautifulSoup(html,'html.parser')
>>>print(s.find_all(class_="title"))
[<p class="title">
                   wemud
               </p>]

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选择器
>>>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
4.1 初始化pyquery对象
>>>from pyquery import PyQuery as pq

>>>html = '''<html>
>>>            <head>
>>>                <title>
>>>                    wemud
>>>                </title>
>>>            </head>
>>>            <body>
>>>                <p class="title">
>>>                    wemud
>>>                </p>
>>>                <p class="content">
>>>                    play free online.
>>>                </p>
>>>                <a id="link1" href="www.wemud.net.cn">
>>>                    click to join
>>>                </a>
>>>            </body>
>>>        </html>'''
>>>page = pq(html) # 初始化对象
>>>print(type(page))
<class 'pyquery.pyquery.PyQuery'>
>>>from pyquery import PyQuery as pq
>>>page = pq(url="http://www.baidu.com")
>>>print(type(page))
<class 'pyquery.pyquery.PyQuery'>
>>>from pyquery import PyQuery as pq
page = pq(filename="test.xml")
print(type(page))
<class 'pyquery.pyquery.PyQuery'>
4.2 CSS选择器
4.2.1 基本用法
>>>page = pq(html)
>>>print(page('body .title'))
<p class="title">
                    wemud
                </p>
>>>print(page('#link1'))
<a id="link1" href="www.wemud.net.cn">
                    click to join
                </a>
4.2.2 查询方法

1) find()函数

  • 查询所有符合条件的子节点。
>>>page = pq(html)
>>>body = page("body")
>>>print(body.find('p'))
<p class="title">
                   wemud
               </p>
               <p class="content">
                   play free online.
               </p>

2) parent()函数

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

3) parents()函数

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

4) siblings()函数

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

5) items()函数

  • 用于获取返回结果的生成器。
>>>page = pq(html)
>>>p = page("p")
>>>print(type(p))
<class 'pyquery.pyquery.PyQuery'>
>>>print(type(p.items()))
<class 'generator'>
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())
               <p class="title">
                   wemud
               </p>
               <p class="content">
                   play free online.
               </p>
               <a id="link1" href="www.wemud.net.cn">
                   click to join
               </a>
4.2.4 节点动态操作

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)
<p class="">
                   wemud
               </p>

2) 变更节点属性

  • 使用attr()方法修改属性。
>>>page = pq(html)
>>>p = page(".content")
>>>p.attr("class","new class")
>>>p.attr("id","new id")
>>>print(p)
<p class="new class" id="new id">
                   play free online.
               </p>
  • 使用text()方法修改纯文本。
>>>page = pq(html)
>>>p = page(".content")
>>>p.text("some new content")
>>>print(p)
<p class="content">some new content</p>
  • 使用html()方法修改html文本。
>>>page = pq(html)
>>>p = page(".content")
>>>p.html("<text>some new content</text>")
>>>print(p)
<p class="content"><text>some new content</text></p>

3) 变更节点

  • 使用append()方法增加节点。
>>>page = pq(html)
>>>body = page("body")
>>>body.append("<p>i'm new here</p>")
>>>print(body)
<body>
               <p class="title">
                   wemud
               </p>
               <p class="content">
                   play free online.
               </p>
               <a id="link1" href="www.wemud.net.cn">
                   click to join
               </a>
           <p>i'm new here</p></body>
  • 使用remove()方法删除节点。
>>>page = pq(html)
>>>p = page(".content")
>>>p.remove()
>>>print(page("body"))
<body>
               <p class="title">
                   wemud
               </p>
                
               <a id="link1" href="www.wemud.net.cn">
                   click to join
               </a>
           </body>
4.2.5 伪类选择器
>>>page = pq(html)
>>>print(page('.title:first-child'))
<p class="title">
                    wemud
                </p>
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('<br/>')[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/')

参考资料



本文作者:大师兄(superkmi)

上一篇下一篇

猜你喜欢

热点阅读