【莫烦】爬虫基础

小编 2026-06-07 阅读:1485 评论:0
正则表达式 正则表达式 (Regular Expression) 又称 RegEx, 是用来匹配字符的一种工具. 在一大串字符中寻找你需要的内容. 它常被用在很多方面, 比如网页爬虫, 文稿...

正则表达式


正则表达式 (Regular Expression) 又称 RegEx, 是用来匹配字符的一种工具. 在一大串字符中寻找你需要的内容. 它常被用在很多方面, 比如网页爬虫, 文稿整理, 数据筛选等等. 最简单的一个例子, 比如我需要爬取网页中每一页的标题. 而网页中的标题常常是这种形式.

<title>我是标题</ title>

而且每个网页的标题各不相同, 我就能使用正则表达式, 用一种简单的匹配方法, 一次性选取出成千上万网页的标题信息. 正则表达式绝对不是一天就能学会和记住的, 因为表达式里面的内容非常多, 强烈建议, 现在这个阶段, 你只需要了解正则里都有些什么, 不用记住, 等到你真正需要用到它的时候, 再反过头来, 好好琢磨琢磨, 那个时候才是你需要训练自己记住这些表达式的时候.

简单的匹配

正则表达式无非就是在做这么一回事. 在文字中找到特定的内容, 比如下面的内容. 我们在 “dog runs to cat” 这句话中寻找是否存在 “cat” 或者 “bird”.

# matching string
pattern1 = \"cat\"
pattern2 = \"bird\"
string = \"dog runs to cat\"
print(pattern1 in string)    # True
print(pattern2 in string)    # False

但是正则表达式绝非不止这样简单的匹配, 它还能做更加高级的内容. 要使用正则表达式, 首先需要调用一个 python 的内置模块re. 然后我们重复上面的步骤, 不过这次使用正则. 可以看出, 如果re.search()找到了结果, 它会返回一个 match 的 object. 如果没有匹配到, 它会返回 None. 这个 re.search()只是 re 中的一个功能, 之后会介绍其它的功能.

import re

# regular expression
pattern1 = \"cat\"
pattern2 = \"bird\"
string = \"dog runs to cat\"
print(re.search(pattern1, string))  # <_sre.SRE_Match object; span=(12, 15), match=\'cat\'>
print(re.search(pattern2, string))  # None

灵活匹配

除了上面的简单匹配, 下面的内容才是正则的核心内容, 使用特殊的 pattern 来灵活匹配需要找的文字.

如果需要找到潜在的多个可能性文字, 我们可以使用[]将可能的字符囊括进来. 比如 [ab]就说明我想要找的字符可以是a 也可以是b. 这里我们还需要注意的是, 建立一个正则的规则, 我们在 pattern 的 “” 前面需要加上一个r用来表示这是正则表达式, 而不是普通字符串. 通过下面这种形式, 如果字符串中出现 “run” 或者是 “ran”, 它都能找到.且返回找到的第一个值

# multiple patterns (\"run\" or \"ran\")
ptn = r\"r[au]n\"       # start with \"r\" means raw string
print(re.search(ptn, \"dog runs to cat\"))    # <_sre.SRE_Match object; span=(4, 7), match=\'run\'>

同样, 中括号[]中还可以是以下这些或者是这些的组合. 比如[A-Z]表示的就是所有大写的英文字母. [0-9a-z]表示可以是数字也可以是任何小写字母.

print(re.search(r\"r[A-Z]n\", \"dog runs to cat\"))     # None
print(re.search(r\"r[a-z]n\", \"dog runs to cat\"))     # <_sre.SRE_Match object; span=(4, 7), match=\'run\'>
print(re.search(r\"r[0-9]n\", \"dog r2ns to cat\"))     # <_sre.SRE_Match object; span=(4, 7), match=\'r2n\'>
print(re.search(r\"r[0-9a-z]n\", \"dog runs to cat r1n\"))  # <_sre.SRE_Match object; span=(4, 7), match=\'run\'>

按类型匹配

除了自己定义规则, 还有很多匹配的规则时提前就给你定义好了的. 下面有一些特殊的匹配类型给大家先总结一下, 然后再上一些例子.

  • \\d : 任何数字
  • \\D : 不是数字
  • \\s : 任何 white space, 如 [\\t\\n\\r\\f\\v]
  • \\S : 不是 white space
  • \\w : 任何大小写字母, 数字和 “” [a-zA-Z0-9]
  • \\W : 不是 \\w
  • \\b : 空白字符 (只在某个字的开头或结尾)
  • \\B : 空白字符 (不在某个字的开头或结尾)
  • \\ : 匹配 \\
  • . : 匹配任何字符 (除了 \\n)
  • ^ : 匹配开头
  • $ : 匹配结尾
  • ? : 前面的字符可有可无
    下面就是具体的举例说明啦.
# \\d : decimal digit
print(re.search(r\"r\\dn\", \"run r4n\"))           # <_sre.SRE_Match object; span=(4, 7), match=\'r4n\'>
# \\D : any non-decimal digit
print(re.search(r\"r\\Dn\", \"run r4n\"))           # <_sre.SRE_Match object; span=(0, 3), match=\'run\'>
# \\s : any white space [\\t\\n\\r\\f\\v]
print(re.search(r\"r\\sn\", \"r\\nn r4n\"))          # <_sre.SRE_Match object; span=(0, 3), match=\'r\\nn\'>
# \\S : opposite to \\s, any non-white space
print(re.search(r\"r\\Sn\", \"r\\nn r4n\"))          # <_sre.SRE_Match object; span=(4, 7), match=\'r4n\'>
# \\w : [a-zA-Z0-9_]
print(re.search(r\"r\\wn\", \"r\\nn r4n\"))          # <_sre.SRE_Match object; span=(4, 7), match=\'r4n\'>
# \\W : opposite to \\w
print(re.search(r\"r\\Wn\", \"r\\nn r4n\"))          # <_sre.SRE_Match object; span=(0, 3), match=\'r\\nn\'>
# \\b : empty string (only at the start or end of the word)
print(re.search(r\"\\bruns\\b\", \"dog runs to cat\"))    # <_sre.SRE_Match object; span=(4, 8), match=\'runs\'>
# \\B : empty string (but not at the start or end of a word)
print(re.search(r\"\\B runs \\B\", \"dog   runs  to cat\"))  # <_sre.SRE_Match object; span=(8, 14), match=\' runs \'>
# \\\\ : match \\
print(re.search(r\"runs\\\\\", \"runs\\ to me\"))     # <_sre.SRE_Match object; span=(0, 5), match=\'runs\\\\\'>
# . : match anything (except \\n)
print(re.search(r\"r.n\", \"r[ns to me\"))         # <_sre.SRE_Match object; span=(0, 3), match=\'r[n\'>
# ^ : match line beginning
print(re.search(r\"^dog\", \"dog runs to cat\"))   # <_sre.SRE_Match object; span=(0, 3), match=\'dog\'>
# $ : match line ending
print(re.search(r\"cat$\", \"dog runs to cat\"))   # <_sre.SRE_Match object; span=(12, 15), match=\'cat\'>
# ? : may or may not occur
print(re.search(r\"Mon(day)?\", \"Monday\"))       # <_sre.SRE_Match object; span=(0, 6), match=\'Monday\'>
print(re.search(r\"Mon(day)?\", \"Mon\"))          # <_sre.SRE_Match object; span=(0, 3), match=\'Mon\'>

如果一个字符串有很多行, 我们想使用 ^ 形式来匹配行开头的字符, 如果用通常的形式是不成功的. 比如下面的 “I” 出现在第二行开头, 但是使用r\"^I\"却匹配不到第二行, 这时候, 我们要使用 另外一个参数, 让re.search()可以对每一行单独处理. 这个参数就是flags=re.M, 或者这样写也行 flags=re.MULTILINE.

string = \"\"\"
dog runs to cat.
I run to dog.
\"\"\"
print(re.search(r\"^I\", string))                 # None
print(re.search(r\"^I\", string, flags=re.M))     # <_sre.SRE_Match object; span=(18, 19), match=\'I\'>

重复匹配

如果我们想让某个规律被重复使用, 在正则里面也是可以实现的, 而且实现的方式还有很多. 具体可以分为这三种:

  • \\*: 重复零次或多次
    -\\+ : 重复一次或多次
  • {n, m}: 重复 n 至 m 次
  • {n} : 重复 n 次
    举例如下:
# * : occur 0 or more times
print(re.search(r\"ab*\", \"a\"))             # <_sre.SRE_Match object; span=(0, 1), match=\'a\'>
print(re.search(r\"ab*\", \"abbbbb\"))        # <_sre.SRE_Match object; span=(0, 6), match=\'abbbbb\'>

# + : occur 1 or more times
print(re.search(r\"ab+\", \"a\"))             # None
print(re.search(r\"ab+\", \"abbbbb\"))        # <_sre.SRE_Match object; span=(0, 6), match=\'abbbbb\'>

# {n, m} : occur n to m times
print(re.search(r\"ab{2,10}\", \"a\"))        # None
print(re.search(r\"ab{2,10}\", \"abbbbb\"))   # <_sre.SRE_Match object; span=(0, 6), match=\'abbbbb\'>

分组

我们甚至可以为找到的内容分组, 使用 () 能轻松实现这件事. 通过分组, 我们能轻松定位所找到的内容. 比如在这个(\\d+)组里, 需要找到的是一些数字, 在 (.+)这个组里, 我们会找到 “Date: “ 后面的所有内容. 当使用 match.group()时, 他会返回所有组里的内容, 而如果给 .group(2) 里加一个数, 它就能定位你需要返回哪个组里的信息.

match = re.search(r\"(\\d+), Dat (e:.+)\", \"ID: 021523, Date: Feb/12/2017\")
print(match.group())                   # 021523, Date: Feb/12/2017
print(match.group(1))                  # 021523
print(match.group(2))                  # e: Feb/12/2017

有时候, 组会很多, 光用数字可能比较难找到自己想要的组, 这时候, 如果有一个名字当做索引, 会是一件很容易的事. 我们字需要在括号的开头写上这样的形式 ?P<名字> 就给这个组定义了一个名字. 然后就能用这个名字找到这个组的内容.

match = re.search(r\"(?P<id>\\d+), Date: (?P<date>.+)\", \"ID: 021523, Date: Feb/12/2017\")
print(match.group(\'id\'))                # 021523
print(match.group(\'date\'))              # Date: Feb/12/2017

findall

前面我们说的都是只找到了最开始匹配上的一项而已, 如果需要找到全部的匹配项, 我们可以使用findall功能. 然后返回一个列表. 注意下面还有一个新的知识点, |是 or 的意思, 要不是前者要不是后者.

# findall
import re
print(re.findall(r\"r(ua)n\", \"run ran ruan ren\")) #[\'ua\']
print(re.findall(r\"(ua)\", \"run ran eua ruan ren\")) #[\'ua\', \'ua\']
print(re.findall(r\"r[ua]n\", \"run ran ruan ren\")) #[\'run\', \'ran\']
# | : or
print(re.findall(r\"r[u|a]n\", \"run ran ruan ren\")) #[\'run\', \'ran\']
print(re.findall(r\"r(u|a)n\", \"run ran ruan ren\"))    # [\'run\', \'ran\']

replace

我们还能通过正则表达式匹配上一些形式的字符串然后再替代掉这些字符串. 使用这种匹配 re.sub(), 将会比 python 自带的 string.replace() 要灵活多变.

print(re.sub(r\"r[au]ns\", \"catches\", \"dog runs to cat\"))     # dog catches to cat

split

再来我们 Python 中有个字符串的分割功能, 比如想获取一句话中所有的单词. 比如 \"a is b\".split(\" \"), 这样它就会产生一个列表来保存所有单词. 但是在正则中, 这种普通的分割也可以做的淋漓精致.

import re
print(re.split(r\"[;\\.]\", \"ac.d;e\"))#[\'ac\', \'d\', \'e\']
print(re.split(r\"[.]\", \"ac.d;e\"))#[\'ac\', \'d;e\']
print(re.split(r\"[\\.]\", \"ac.d;e\"))#[\'ac\', \'d;e\']
print(re.split(r\"(\\.)\", \"ac.d;e\"))#[\'ac\', \'.\', \'d;e\']
print(re.split(r\"(.)\", \"ac.d;e\"))#[\'\', \'a\', \'\', \'c\', \'\', \'.\', \'\', \'d\', \'\', \';\', \'\', \'e\', \'\']

compile

最后, 我们还能使用 compile 过后的正则, 来对这个正则重复使用. 先将正则 compile 进一个变量, 比如 compiled_re, 然后直接使用这个 compiled_re来搜索.

compiled_re = re.compile(r\"r[ua]n\")
print(compiled_re.search(\"dog ran to cat\"))  # <_sre.SRE_Match object; span=(4, 7), match=\'ran\'>

小抄

为了大家方便记忆, 我很久以前在网上找到了一份小抄, 这个小抄的原出处应该是这里. 小抄很有用, 不记得的时候回头方便看.
\"在这里插入图片描述\"

爬虫简介

why?

其实你身边到处都是爬虫的产物, 比如说搜索引擎 (Google, 百度), 他们能为你提供这么多搜索结果, 也都是因为它们爬了很多信息, 然后展示给你. 再来说一些商业爬虫, 比如爬爬淘宝的同类商品的价格信息, 好为自己的商品挑选合适的价格. 爬虫的用途很多很多, 如果你搞机器学习, 爬虫就是你获取数据的一种途径, 网上的信息成百上千, 只要你懂爬虫, 你都能轻松获取.

我做过的一些有趣的爬虫

https://morvanzhou.github.io/tutorials/data-manipulation/scraping/1-00-why/)

为什么做这个教程

这个教程提供了你一次入门的机会, 当然网上还有很多很好的入门教程, 比如:

崔庆才 的Python爬虫学习系列教程
知乎问答中的各种推荐
孔淼 的一看就明白的爬虫入门讲解
这些都是非常好的参考资料, 你的学习请不要只限于一个网站. 因为像机器学习一样, 爬虫也囊括的东西绝非不止一点点. 而你为什么要看看我的爬虫教程呢? 因为我只关注基础, 我认为入门是最重要的, 能帮你成功引上路子, 我想你会轻松很多. 而且搭配视频讲解的形式, 也会更加容易理解. 是为初学者定制的. 如果你已经入过门, 想着如何商业化爬虫, 这个教程应该不能满足你的需求了.

当我第一次接触爬虫的时候, 其实很陌生, 完全不知道从何开始. 在网上自己搜一些介绍, 但是他们的介绍都太笼统了, 给你丢几个关键词, 让你自己解决. 当时我就懵逼了, 看着那些关键词 (requests, urllib, beautifulsoup, scrapy) 不知道从何下手, 我估计你也会有这种感觉. 当时, 我花了大把的精力, 想弄懂这些东西和爬虫的关系. 而且分清如果只想入门, 我们需要掌握哪些? 毕竟商业化和入门还是有很大不同的. 有些关键词或者模块是为了商业化而用的. 所以我花了很多时间, 整理网上的这些信息. 总结出一条入门爬虫的便捷之路.

课程介绍

这系列教程按照上面的逻辑来教会你爬虫, 我们会从网页的基本结构开始讲述, 慢慢使用一些简单的工具, 做一些简单的爬虫. 还会有一些小练习, 让你爬爬真正的互联网. 下载美图, 逛逛百度百科, 全网爬取等等. 当你懂得了爬虫的概念, 我们在深入一些, 谈谈如何加速你那和蠕虫(爬的慢)一样的爬虫, 把它升级为一只小飞虫(多进程,异步爬取). 当然这些内容都不会特别深入, 重点是把你带入门. 但是我会在每节内容里加一些链接, 提供给想要深入了解的朋友们.

下次我们就正式开始学习爬虫啦.

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表