Python 爬虫找到数据了 re & XPath &a

作者: CoderHG | 来源:发表于2018-05-20 13:18 被阅读182次

    是的,爬虫就是为了获取数据。在获取的数据中,会有很多的冗余信息,需要在获取的数据中提取所需要的有用信息。进而联想到数据的匹配:正则表达式。接下来重点介绍的是 Python 中的两个提取数据的两个框架 reXPath

    一、正则表达式

    正则表达式是通用的,不区分任何的语言。以下是一些比较常用的通配符:


    image.png

    二、re

    re 中有三个比较重要的函数,介绍如下:

    • findall: 匹配所有符合规律的内容,返回包含结果的列表
    • Search:匹配并提取第一个符合规律的内容,返回一个正则表达式对象(object)
    • Sub :替换符合规律的内容,返回替换后的值

    接下来是将一些比较常用的语法通过以上三个函数都使用以下。当然了, 在使用之前先导入 re

    import re
    

    2.1 关于点、星号与问号的使用

    # 点的使用
    def dotFunc():
        # 定义一个字符串
        value = 'Hello, CoderHG. My name is CoderOC.'
        # 匹配名字
        names = re.findall('Coder..', value)
        # 打印
        print(names)
        # 结果 ['CoderHG', 'CoderOC']
        # 依次打印
        for name in names:
            print(name)
    
    
    # 星号的使用
    def starFunc():
        value = 'Hello, CoderHG.'
        name = re.findall('Coder*', value)
        print(name)
        # 结果  ['Coder']
    
    # 问号的使用
    def questionFunc():
        value = 'Hello, CoderHG.'
        name = re.findall('Coder?', value)
        print(name)
        # 结果  ['Coder']
    

    2.2 贪心算法

    主要是 .* 匹配, 代码如下:

    ### 2.2 贪心算法
    def tanxinFunc():
        secret_code = 'hadkfalifexxIxxfasdjifja134xxlovexx23345sdfxxyouxx8dfse'
        b = re.findall('xx.*xx',secret_code)
        print(b)
        # 打印结果 ['xxIxxfasdjifja134xxlovexx23345sdfxxyouxx']
    

    匹配的是尽量多的内容。

    2.3 非贪心算法

    主要是匹配 .*?匹配, 代码如下:

    # 非贪心算法
    def notTanxinFunc():
        secret_code = 'hadkfalifexxIxxfasdjifja134xxlovexx23345sdfxxyouxx8dfse'
        b = re.findall('xx.*?xx',secret_code)
        print(b)
        # 打印结果 ['xxIxx', 'xxlovexx', 'xxyouxx']
    

    尽量的少,尽量的细致的去匹配

    2.4 括号+非贪心算法

    # 括号+非贪心算法
    def kuohaoNotTanxinFunc():
        secret_code = 'hadkfalifexxIxxfasdjifja134xxlovexx23345sdfxxyouxx8dfse'
        b = re.findall('xx(.*?)xx', secret_code)
        print(b)
        # 打印结果 ['I', 'love', 'you']
    
        for text in b:
            print(text)
    

    2.5 re.S 参数的作用

    def huanhangFunc():
        s = '''sdfxxhe
        llo
        xxfsdfxxworldxxasdf'''
        # 不带 re.S 参数
        d = re.findall('xx(.*?)xx', s)
        print(d)
        # 打印结果: ['fsdf']
    
        # 带有 re.S 参数
        d_reS = re.findall('xx(.*?)xx',s, re.S)
        print(d_reS)
        # 打印结果: ['he\n    llo\n    ', 'world']
    

    加上 re.S 能匹配出带有换行的字符。

    2.6 findall 与 search 结合使用

    def findallAndSearchFunc():
        s2 = 'asdfxxIxx123xxlovexxdfd'
        result = re.search('xx(.*?)xx123xx(.*?)xx',s2)
        print(result)
        # 打印结果 对象: <_sre.SRE_Match object; span=(4, 20), match='xxIxx123xxlovexx'>
        f = result.group(2)
        print(f)
        # 打印结果 : love
    
        result = re.findall('xx(.*?)xx123xx(.*?)xx',s2)
        print(result)
        # 打印结果 数组套元组: [('I', 'love')]
        f = result[0][1]
        print(f)
        # 打印结果 :  love
    

    2.7 sub 的使用

    # sub 的使用, 替换字符串
    def subFunc():
        s = '我很想对她说: I what You.'
        f = re.sub('I (.*?) You.', 'I love You.', s)
    
        print(f)
        # 打印结果: 我很想对她说: I love You.
    

    三、XPath

    3.1 安装 lxml

    通过 pip list 指令查看是否已经安装 list。 如果没有安装,执行 pip install lxml 即可。
    这个东西与其他的库有所不同, 说的是 XPath, 安装的是 lxlml。在使用的时候, 直接导入:

    from lxml import etree
    

    温馨提示:按照以上的步骤如果import 的时候出错, 那么到设置中手动安装一下。

    3.2 使用格式

    3.2.1 套路

    Selector = etree.HTML(网页源代码)
    Selector.xpath(一段神奇的符号)

    3.2.2 规律

      1. 树状结构
      1. 逐层展开
      1. 逐层定位
    • 4.寻找独立节点

    3.2.3 语法

      1. // 定位根节点
      1. / 往下层寻找
      1. 提取文本内容:/text()
      1. 提取属性内容: /@xxxx

    3.3 实例

    3.3.1 常规用法

    这里有一个简单的 HTML 文本, 如下:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>测试-常规用法</title>
    </head>
    <body>
    <div id="content">
        <ul id="useful">
            <li>这是第一条信息</li>
            <li>这是第二条信息</li>
            <li>这是第三条信息</li>
        </ul>
        <ul id="useless">
            <li>不需要的信息1</li>
            <li>不需要的信息2</li>
            <li>不需要的信息3</li>
        </ul>
    
        <div id="url">
            <a href="http://jikexueyuan.com">极客学院</a>
            <a href="http://jikexueyuan.com/course/" title="极客学院课程库">点我打开课程库</a>
        </div>
    </div>
    
    </body>
    </html>
    

    现在想要提取 id = useful 中的 ui 标签中的内容, 以及 获取 a 标签中的连接,如果使用 re 的话, 那就不好办了, 尤其是提取 id = useful 中的 ui 标签中的内容,因为还有一个与之类似的 id=userless 的标签。
    使用 XPath 的话, 就显得容易了, 代码如下:

    # XPath 的相关用法
    def xpatHTML():
        # 获取一个与 XPath 相关的对象
        selector = etree.HTML(html)
        # 提取文本 是数组
        contents = selector.xpath('//ul[@id="useful"]/li/text()')
        # content = selector.xpath('//*[@id="useful"]/li/text()')
        # 打印获取的内容
        for content in contents:
            print(content)
    
        # 打印结果:
        # 这是第一条信息
        # 这是第二条信息
        # 这是第三条信息
    
    
        # 提取属性
        links = selector.xpath('//a/@href')
        for link in  links:
            print(link)
    
        # 打印结果:
        # http://jikexueyuan.com
        # http://jikexueyuan.com/course/
    

    3.3.2 特殊用法一(以相同的字符开头)

    现在有以下一段 html 的文本内容:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <div id="test-1">需要的内容1</div>
        <div id="test-2">需要的内容2</div>
        <div id="testfault">需要的内容3</div>
    </body>
    </html>
    

    需要提取 id=‘test*’ 中的内容, 那么久需要使用到的的语法是这样的:

    starts-with(@属性名称, 属性字符相同部分)

    代码可见:

    # 以相同的字符开头
    def startwithFunc():
        print('以相同的字符开头')
        # 获取一个与 XPath 相关的对象
        selector = etree.HTML(html)
        # 提取文本 是数组
        contents = selector.xpath('//div[starts-with(@id,"test")]/text()')
    
        # 打印
        for content in contents:
            print(content)
    
        # 打印结果:
        # 需要的内容1
        # 需要的内容2
        # 需要的内容3
    

    3.3.3 特殊用法二(标签套标签)

    有一个如下的 HTML 文本:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <div id="test3">
            我左青龙,
            <span id="tiger">
                右白虎,
                <ul>上朱雀,
                    <li>下玄武。</li>
                </ul>
                老牛在当中,
            </span>
            龙头在胸口。
        </div>
    </body>
    </html>
    

    现在想要提取这样的内容:我左青龙,右白虎,上朱雀,下玄武。老牛在当中,龙头在胸口。
    那么就要使用 string(.), 实现代码如下:

    # 标签套标签
    def divdivFunc():
        print('标签套标签')
        # 获取一个与 XPath 相关的对象
        selector = etree.HTML(html)
        # 提取文本
        contents = selector.xpath('//div[@id="test3"]/text()')
    
        # 打印
        print(contents)
        # 打印结果: ['\n        我左青龙,\n        ', '\n        龙头在胸口。\n    ']
        # 这里的结果没有打印出标签中的标签的内容
    
        # 还需要这么做
        datas = selector.xpath('//div[@id="test3"]')
        # 获取数组中的第一个元素
        data = datas[0]
        # 获取内容
        info = data.xpath('string(.)')
        # 字符串替换  '\n'  ->  ''
        content = info.replace('\n', '')
    
        # 字符串替换  '  '  ->  ''
        content = content.replace(' ', '')
    
        print(content)
        # 打印结果: 我左青龙,右白虎,上朱雀,下玄武。老牛在当中,龙头在胸口。
    

    小总结

    正在表达式能处理很多的问题了,但是有的时候也会遇到以上 XPath 中介绍的数据结构, 如果还是使用正则表达式的话, 就有点不简单的。以上可以看出使用 XPath 还是很简单的。

    四、Python 的并行化

    以一个异步获取贴吧数据为例:

    #-*-coding:utf8-*-
    # as 的语法是将 Pool 替换成 ThreadPool 来使用
    from multiprocessing.dummy import Pool as ThreadPool
    import multiprocessing
    
    import requests
    import time
    
    # 存储所有的 url
    urls = []
    
    # 获取所有的 url
    def getURLs():
        for i in range(1, 21):
            # 生成连接
            newpage = 'http://tieba.baidu.com/p/3522395718?pn=' + str(i)
            # 添加到 urls 列表中
            urls.append(newpage)
    
    
    # 爬去数据
    def getsource(url):
        requests.get(url)
        print(url)
    
    # 单线程获取数据
    def sigleFunc():
        startTime = time.time()
        for url in  urls:
            getsource(url)
    
        endTime = time.time()
    
        print(u'单线程耗时' + str(endTime-startTime))
    
    
    # 并行处理
    def poolFunc():
        count = multiprocessing.cpu_count();
        print(count)
    
        startTime = time.time()
    
        pool = ThreadPool(count)
        results = pool.map(getsource, urls)
        pool.close()
        pool.join()
    
        endTime = time.time()
    
        print(u'耗时' + str(endTime - startTime))
    
    
    
    if __name__ == '__main__':
        # 获取所有的 URL
        getURLs()
        # 单线程
        sigleFunc()
        # 多线程(并行)
        poolFunc()
    

    Log日志输入如下:

    http://tieba.baidu.com/p/3522395718?pn=1
    http://tieba.baidu.com/p/3522395718?pn=2
    http://tieba.baidu.com/p/3522395718?pn=3
    http://tieba.baidu.com/p/3522395718?pn=4
    http://tieba.baidu.com/p/3522395718?pn=5
    http://tieba.baidu.com/p/3522395718?pn=6
    http://tieba.baidu.com/p/3522395718?pn=7
    http://tieba.baidu.com/p/3522395718?pn=8
    http://tieba.baidu.com/p/3522395718?pn=9
    http://tieba.baidu.com/p/3522395718?pn=10
    http://tieba.baidu.com/p/3522395718?pn=11
    http://tieba.baidu.com/p/3522395718?pn=12
    http://tieba.baidu.com/p/3522395718?pn=13
    http://tieba.baidu.com/p/3522395718?pn=14
    http://tieba.baidu.com/p/3522395718?pn=15
    http://tieba.baidu.com/p/3522395718?pn=16
    http://tieba.baidu.com/p/3522395718?pn=17
    http://tieba.baidu.com/p/3522395718?pn=18
    http://tieba.baidu.com/p/3522395718?pn=19
    http://tieba.baidu.com/p/3522395718?pn=20
    单线程耗时18.206058025360107
    8
    http://tieba.baidu.com/p/3522395718?pn=3
    http://tieba.baidu.com/p/3522395718?pn=8
    http://tieba.baidu.com/p/3522395718?pn=2
    http://tieba.baidu.com/p/3522395718?pn=5
    http://tieba.baidu.com/p/3522395718?pn=6
    http://tieba.baidu.com/p/3522395718?pn=7
    http://tieba.baidu.com/p/3522395718?pn=4
    http://tieba.baidu.com/p/3522395718?pn=1
    http://tieba.baidu.com/p/3522395718?pn=14
    http://tieba.baidu.com/p/3522395718?pn=13
    http://tieba.baidu.com/p/3522395718?pn=10
    http://tieba.baidu.com/p/3522395718?pn=12
    http://tieba.baidu.com/p/3522395718?pn=9
    http://tieba.baidu.com/p/3522395718?pn=15
    http://tieba.baidu.com/p/3522395718?pn=11
    http://tieba.baidu.com/p/3522395718?pn=16
    http://tieba.baidu.com/p/3522395718?pn=19
    http://tieba.baidu.com/p/3522395718?pn=18
    http://tieba.baidu.com/p/3522395718?pn=20
    http://tieba.baidu.com/p/3522395718?pn=17
    耗时2.678765058517456
    

    结论: 使用并行处理, 可以节省不少的时间。

    温馨提示:本篇介绍,来自于 极客学院 的学习记录。感谢极客学院课程!

    这些都是基础中的基础、谢谢阅读!

    相关文章

      网友评论

        本文标题:Python 爬虫找到数据了 re & XPath &a

        本文链接:https://www.haomeiwen.com/subject/cswodftx.html