Python 爬虫找到数据了 re & XPath & requests & Pool

是的,爬虫就是为了获取数据。在获取的数据中,会有很多的冗余信息,需要在获取的数据中提取所需要的有用信息。进而联想到数据的匹配:正则表达式。接下来重点介绍的是 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

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容