爬虫(二) 数据采集和解析

数据采集和解析

通过上一个文章的学习, 我们已经了解到了开发一个爬虫需要做的工作以及一些常见的问题, 至此我们可以对爬虫开发需要做个的工作以及相关的技术做一个简单的汇总, 可能有些库我们之前并没有使用过, 不过别担心, 这些内容我们都会讲到的.

1. 下载数据 -urllib/ requests/ aiohttp.
2. 解析数据 -re/ lxml/ beautifulsoup4(bs4)/ pyquery.
3. 缓存和持久化 -pymysql/ redis/ sqlalchemy/ peewee/ pymongo.
4. 生成摘要 - hashlib.
5. 序列化和压缩 -pickle/ json/ zlib.
6. 调度器 -进程/ 线程/ 协程.

序列化 - 把对象变成字符,或者字节序列 pickle.dump/dumps
反序列化 - 把字节或者字符序列还原为对象 pickle.load/loads

SQL数据库中事务的隔离级别:
1. UNcommitted read 读未提交,并发性好 -但是会出现读脏数据Dirty Read
2. read Committed - 可以避免脏读,但是不可以重复读, - UNrepeatable read
3. repeatable read 可以重复读 – 但是会出现Phantom Read 幻读
4. serializable -串行,没有并发了
永久更改事务隔离级别: set global transaction isolation level read committed;
临时更改事务隔离级别: set session transaction isolation level serializable;

为什么使用非关系型数据库 redis/MongoDB
CAP理论 
对于分布式计算系统,不可能同时满足一下三点: 
1. 一致性(consistency)(所有节点在同一时间具有相同的数据) 
2. 可用性(availability)(保证每个请求不管成功还是失败都有响应) 
3. 分割容忍(Partitiontolerance)(系统中任意信息的丢失或者失败不会影响系统继续运行)

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类: 
* CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。 
* CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。 
* AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
HTML页面解析
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <h1>Hello, world!</h1>
        <p>这是一个神奇的网站!</p>
        <hr>
        <div>
            <h2>这是一个例子程序</h2>
            <p>静夜思</p>
            <p class="foo">床前明月光</p>
            <p id="bar">疑似地上霜</p>
            <p class="foo">举头望明月</p>
            <div><a href="http://www.baidu.com"><p>低头思故乡</p></a></div>
        </div>
        <a class="foo" href="http://www.qq.com">腾讯网</a>
        <img src="./img/pretty-girl.png" alt="美女">
        <img src="./img/hellokitty.png" alt="凯蒂猫">
        <img src="/static/img/pretty-girl.png" alt="美女">
        <table>
            <tr>
                <th>姓名</th>
                <th>上场时间</th>
                <th>得分</th>
                <th>篮板</th>
                <th>助攻</th>
            </tr>
        </table>
    </body>
</html>

如果你对上面的代码并不感到陌生, 那么你一定知道HTML页面通常由三部分构成, 分别是: 用来承载内容的Tag(标签), 负责渲染页面的CSS(层叠样式表) 以及控制交互式行为的JavaScript. 通常, 我们可以在浏览器的右键菜单中通过’查看网页源代码’的方式获取网页的代码并了解页面的结构; 当然, 我们也可以通过浏览器提供的开发人员工具来了解网页更多的信息.

使用requests获取页面
  1. GET请求和POST请求.
  2. URL参数和请求头
  3. 复杂的POST请求(如文件上传).
  4. 操作Cookie.
  5. 设置代理服务器.
  6. 超时设置.

说明: 关于requests的详细用法可以参考它的官方文档.

四种采集方式

四中采集方式的比较
抓取方法 速度 使用难度 备注
正则表达式 困难 常用正则表达式
在线正则表达式测试
lxml 一般 需要安装C语言依赖库
唯一支持XML的解析器
Beautiful 快/慢(取决于解析器) 简单
PyQuery 较快 简单 Python版的jQuery

说明:Beautiful的解析器包括:Python标准库(html.parser)、lxml的HTML解析器、lxml的XML解析器和html5lib。

BeautifulSoup的使用 (主要了解这个并利用lxml进行解析的方式)
  1. 遍历文档树
    • 获取标签
    • 获取标签属性
    • 获取标签内容
    • 获取子(孙)节点
    • 获取父节点/祖先节点
    • 获取兄弟节点

2.搜索树节点
- find/find_all: 字符串, 正则表达式, 列表, True, 函数或Lambda.
- select_one/ select:CSS选择器
- find和select_one获取的都是一个节点
- find_all与select获取的是多个节点,以列表形式返回

说明:更多内容可以参考BeautifulSoup的官方文档

from bs4 import BeautifulSoup
import re

def main():
# 这里为了方便演示,直接获取了一个html的页面源码,并且赋值给html
    html = """
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>首页</title>
        </head>
        <body>
            <h1>Hello, world!</h1>
            <p>这是一个<em>神奇</em>的网站!</p>
            <hr>
            <div>
                <h2>这是一个例子程序</h2>
                <p>静夜思</p>
                <p class="foo">床前明月光</p>
                <p id="bar">疑似地上霜</p>
                <p class="foo">举头望明月</p>
                <div><a href="http://www.baidu.com"><p>低头思故乡</p></a></div>
            </div>
            <a class="foo" href="http://www.qq.com">腾讯网</a>
            <img src="./img/pretty-girl.png" alt="美女">
            <img src="./img/hellokitty.png" alt="凯蒂猫">
            <img src="/static/img/pretty-girl.png" alt="美女">
            <table>
                <tr>
                    <th>姓名</th>
                    <th>上场时间</th>
                    <th>得分</th>
                    <th>篮板</th>
                    <th>助攻</th>
                </tr>
            </table>
        </body>
    </html>
    """
    # 通过BeautifulSoup构建一个DOM对象,(可通过参数指定解析器,解析器优劣见上文)
    # 之后我们就可以对soup对象进行各种寻根问祖的节点操作  
    soup = BeautifulSoup(html, 'lxml')
    # 类似于JavaScript中 - document.title  

    # 遍历文档树的办法  
    print(soup.title) # 可以直接获取title标签
    # <title>首页</title>

    # 类似于JavaScript中 - document.body.h1
    print(soup.p) # 获取第一个p标签,只能获取一个
    # <p>这是一个<em>神奇</em>的网站!</p>

    print(soup.body.p.text) # 获取body标签下p标签的内容text
    # 这是一个神奇的网站!

    print(soup.body.p.contents) # 获取body标签下p标签的所有直接子节点并且以列表形式返回
    # ['这是一个', <em>神奇</em>, '的网站!']

    # .children方法获取了p标签的所有直接子节点
    for p_child in soup.body.p.children:
        print(p_child)
    # .contents与.children方法类似,只是.contents返回列表,.children返回一个生成器,需要进行遍历才能拿到相应的数据。.descendants则拿到的是所有的子孙节点

    print(len([elem for elem in soup.body.children]))  # 获取body下所有直接子节点数量
    # 19
    print(len([elem for elem in soup.body.descendants]))# 获取body下所有的子节点的数量
    # 65
    print([elem for elem in soup.body.descendants]) # 获取body下所有的子节点 
    ####  

    # 使用find与find_all()函数的办法
    # --正则表达式--
    # findAll与find_all都是相同的函数,只是写法不同,find函数找到的是一个对象,
    print(soup.findAll(re.compile(r'^h[1-6]'))) # 获取所有的h1-h6的标签

    print(soup.body.find_all(r'^h')) # 获取body标签下所有以h打头的标签

    print(soup.body.div.find_all(re.compile(r'^h'))) # 获取body标签下第一个div标签中所有的h打头的标签

    print(soup.find_all(re.compile(r'r$'))) # 获取所有以r结尾的标签

    # 获取所有标签为‘img’,‘src’属性符合正则表达式(r'\./img/\w+.png')的标签
    print(soup.find_all('img', {'src':re.compile(r'\./img/\w+.png')}))  

    # --函数/lambda--
    # 获取有两个属性attrs的所有标签
    print(soup.find_all(lambda x: len(x.attrs) == 2))

    # 获取所有的foo标签
    print(soup.find_all(foo))

    # 获取所有class=foo的p标签
    print(soup.find_all('p', {'class': 'foo'}))

    # 获取所有的a标签的href属性,并对其进行遍历
    for elem in soup.select('a[href]'):
        print(elem.attrs['href'])

    # 获取class=”hello1“的div 下的所有a标签 的所有p标签
    print(soup.select('div[class="hello1"] a p'))
    # [<p>疑似地上霜</p>, <p>低头思故乡</p>]

    # 获取class=”hello1“的div, 结果是一个列表【***】
    print(soup.select('div[class="hello1"]'))

    # 获取class=”hello1“的div 下的所有a标签
    print(soup.select('div[class="hello1"] a'))
    # [<a href="http://www.baidu.com"><p>低头思故乡</p></a>]

    # 获取class=”hello1“的div 下的所有a标签
    print(soup.select_one('div[class="hello1"] a'))
    # <a href="http://www.baidu.com"><p>低头思故乡</p></a>

def foo(elem):
    return len(elem.attrs) == 2

if __name__ == '__main__':
    main()

实例 - 获取知乎发现上的问题链接

from urllib.parse import urljoin

import re
import requests

from bs4 import BeautifulSoup


def main():
    headers = {'user-agent': 'Baiduspider'}
    proxies = {
        'http': 'http://122.114.31.177:808'
    }
    base_url = 'https://www.zhihu.com/'
    seed_url = urljoin(base_url, 'explore')
    resp = requests.get(seed_url,
                        headers=headers,
                        proxies=proxies)
    soup = BeautifulSoup(resp.text, 'lxml')
    href_regex = re.compile(r'^/question')
    link_set = set()
    for a_tag in soup.find_all('a', {'href': href_regex}):
        if 'href' in a_tag.attrs:
            href = a_tag.attrs['href']
            full_url = urljoin(base_url, href)
            link_set.add(full_url)
    print('Total %d question pages found.' % len(link_set))


if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/qq_41637554/article/details/80548402