参考 B 站IT 私塾 的Python 爬虫编程基础 5 天速成(2021 全新合集)Python 入门+数据分析 视频
初识网络爬虫 网络爬虫 (又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动地抓取互联网信息的程序或者脚本。
其本质 是,由一个入口网页进行深入,不停的进行其他的 URL 的爬取,然后再把抓取到的网页进行分析处理得出想要的数据。
Robots 协议 ,是网站跟爬虫间的协议,用简单直接的 txt 格式文本方式告诉对应的爬虫被允许的权限
网络爬虫的一般流程 发起请求,获取响应
通过 http 库,对目标站点进行请求。等同于自己打开浏览器,输入网址
常用库:urllib、urllib3、requests
服务器会返回请求的内容,一般为:html、二进制文件(视频,音频)、文档,json 字符串
解析内容
寻找自己需要的信息,就是利用正则表达式或者其他库提取目标信息
常用库:re、beautifulsoup4
数据持久化 将解析得到的数据保存到文件或者数据库中
基本流程 准备工作 分析页面 借助 Chrome 开发者工具(F12)来分析网页,在 Elements 下找到需要的数据位置
编码规范 一般 Python 程序第一行需要加入:#-*- coding:utf-8 -*-
或者# coding=utf-8
。这样可以在代码中包含中文 使用函数实现单一功能或相关联功能的代码段,可以提高可读性和代码重复利用率,函数代码块以 def 关键词开头。 可以加入 main 函数用于测试程序:if __name__ == "__main__":
使用 # 添加注释,说明代码段的作用 引入模块 模块 module :用来从逻辑上组织 Python 代码(变量、函数、类),本质就是 py 文件,提高代码的可维护性。
module 可看作一个工具类,可共用或者隐藏代码细节,将相关代码放置在一个 module 以便让代码更好用、易懂,让 coder 重点放在高层逻辑上。
module 能定义函数、类、变量,也能包含可执行的代码。module 来源有 3 种:
Python 内置的模块(标准库)
第三方模块
自定义模块
包 package : 为避免模块名冲突,Python 引入了按目录组织模块的方法。包是含有 Python 模块的文件夹。
例子:
1 2 import sysfrom bs4 import BeautifulSoup
获取数据 对每一个页面,调用 askURL 函数获取页面内容 定义一个获取页面的函数 askURL,传入一个 url 参数,表示网址 urllib.Request
生成请求。urllib.urlopen
发送请求获取响应。read 获取页面内容。在访问页面时经常会出现错误,为了程序正常运行,加入异常捕获try...except...
语句 urllib 是 python 标准库,直接使用
request.urlopen()
:接收一个 url 或一个 request 对象,向目标发起请求
1 2 3 4 5 6 7 8 9 10 11 12 13 from urllib import requesturl = "https://www.baidu.com" response = request.urlopen(url) print(response.geturl()) print(response.getcode()) print(response.info()) html = response.read() html.decode("utf-8" ) print(html)
常见的HTTP状态码 状态码 英文名称 中文描述 200 OK 请求成功。一般用于 GET 与 POST 请求 301 Moved Permanently 永久移动。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 500 Internal Server Error 服务器内部错误,无法完成请求
想了解状态码更多?
urllib 模块 这最基本的请求,是 python 内置的一个 http 请求库,不需要额外的安装。只需要关注请求的链接、参数,提供了强大的解析。
urllb.request 请求模块
urllib.error 异常处理模块
urllib.parse 解析模块
用法讲解 :
简单的一个 get 请求: 1 2 3 import urllib.requestreponse = urllib.request.urlopen('http://www.baidu.com' ) print(reponse.read().decode('utf-8' ))
简单的一个 post 请求 1 2 3 4 5 import urllib.parseimport urllib.requestdata = bytes (urllib.parse.urlencode({'hello' :'world' }),encoding='utf-8' ) reponse = urllib.request.urlopen('http://httpbin.org/post' ,data=data) print(reponse.read())
超时处理 1 2 3 import urllib.requestresponse = urllib.request.urlopen('http://httpbin.org/get' ,timeout=1 ) print(response.read())
由于使用 urlopen 无法传入参数,我们需要声明一个 request 对象,通过这个对象来添加参数 1 2 3 4 import urllib.requestrequest = urllib.request.Request('https://python.org' ) response = urllib.request.urlopen(request) print(response.read().decode('utf-8' ))
我们还可以分别创建字符串、字典等等来带入到 request 对象里面 1 2 3 4 5 6 7 8 9 10 11 12 13 from urllib import request,parseurl='http://httpbin.org/post' headers={ 'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' , 'Host' :'httpbin.org' } dict ={ 'name' :'jay' } data = bytes (parse.urlencode(dict ),encoding='utf-8' ) req=request.Request(url=url,data=data,headers=headers,method='POST' ) response=request.urlopen(req) print(response.read().decode('utf-8' ))
还可通过 addheaders 方法不断地向原始的 requests 对象里不断添加 1 2 3 4 5 6 7 8 9 10 from urllib import request,parseurl ='http://httpbin.org/post' dict = { 'name' :'cq' } data=bytes (parse.urlencode(dict ),encoding='utf-8' ) req = request.Request(url=url,data=data,method='POST' ) req.add_header('user-agent' , 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' ) response=request.urlopen(req) print(response.read().decode('utf-8' )
解析内容 使用 BeautifulSoup 定位特定的标签位置 使用正则表达式找到具体的内容 标签解析 BeautifulSoup 是一个库,提供一些简单的、python 式的用来处理导航、搜索、修改分析树 等功能,通过解析文档为用户提供需要抓取的数据。我们需要的每个电影都在一个<div>的标签中,且每个 div 标签都有一个属性 class= "item"。
BeautifulSoup 模块 简介 BeautifulSoup4 和 lxml 一样,Beautiful Soup 也是一个 HTML/XML 的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
BeautifulSoup 支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python 默认的解析器,lxml 解析器更加强大,速度更快,推荐使用 lxml 解析器。
Beautiful Soup 自动将输入文档转换为 Unicode 编码,输出文档转换为 utf-8 编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup 就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。
使用 假设有这样一个 Html,具体内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <!DOCTYPE html > <html > <head > <meta content ="text/html;charset=utf-8" http-equiv ="content-type" /> <meta content ="IE=Edge" http-equiv ="X-UA-Compatible" /> <meta content ="always" name ="referrer" /> <link href ="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel ="stylesheet" type ="text/css" /> <title > 百度一下,你就知道</title > </head > <body link ="#0000cc" > <div id ="wrapper" > <div id ="head" > <div class ="head_wrapper" > <div id ="u1" > <a class ="mnav" href ="http://news.baidu.com" name ="tj_trnews" > 新闻 </a > <a class ="mnav" href ="https://www.hao123.com" name ="tj_trhao123" > hao123 </a > <a class ="mnav" href ="http://map.baidu.com" name ="tj_trmap" > 地图 </a > <a class ="mnav" href ="http://v.baidu.com" name ="tj_trvideo" > 视频 </a > <a class ="mnav" href ="http://tieba.baidu.com" name ="tj_trtieba" > 贴吧 </a > <a class ="bri" href ="//www.baidu.com/more/" name ="tj_briicon" style ="display: block;" > 更多产品 </a > </div > </div > </div > </div > </body > </html >
创建 beautifulsoup4 对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(bs.prettify()) print(bs.title) print(bs.title.name) print(bs.title.string) print(bs.head) print(bs.div) print(bs.div["id" ]) print(bs.a) print(bs.find_all("a" )) print(bs.find(id ="u1" )) for item in bs.find_all("a" ): print(item.get("href" )) for item in bs.find_all("a" ): print(item.get_text())
BeautifulSoup4 四大对象种类 BeautifulSoup4 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,所有对象可以归纳为 4 种:
Tag NavigableString BeautifulSoup Comment Tag Tag 通俗点讲就是 HTML 中的一个个标签,例如:
1 2 3 4 5 6 7 8 9 10 11 12 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(bs.title) print(bs.head) print(bs.a) print(type (bs.a))
我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是 bs4.element.Tag。但是注意,它查找的是在所有内容中的第一个符合要求的标签。
对于 Tag,它有两个重要的属性,是 name 和 attrs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(bs.name) print(bs.head.name) print(bs.a.attrs) print(bs.a['class' ]) bs.a['class' ] = "newClass" print(bs.a) del bs.a['class' ]print(bs.a)
NavigableString 既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如:
1 2 3 4 5 6 7 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(bs.title.string) print(type (bs.title.string))
BeautifulSoup BeautifulSoup 对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性,例如:
1 2 3 4 5 6 7 8 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(type (bs.name)) print(bs.name) print(bs.attrs)
Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。
1 2 3 4 5 6 7 8 9 from bs4 import BeautifulSoupfile = open ('./aa.html' , 'rb' ) html = file.read() bs = BeautifulSoup(html,"html.parser" ) print(bs.a) print(bs.a.string) print(type (bs.a.string))
遍历文档树 功能 .contents 获取 Tag 的所有子节点,返回一个 list .children 获取 Tag 的所有子节点,返回一个生成器 .descendants 获取 Tag 的所有子孙节点 .strings 如果 Tag 包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历 .stripped_strings 与 strings 用法一致,只不过可以去除掉那些多余的空白内容 .parent 获取 Tag 的父节点 .parents 递归得到父辈元素的所有节点,返回一个生成器 .previous_sibling 获取当前 Tag 的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符 .next_sibling 获取当前 Tag 的下一个节点,属性通常是字符串或空白,真是结果是当前标签与下一个标签之间的顿号与换行符 .previous_siblings 获取当前 Tag 的上面所有的兄弟节点,返回一个生成器 .next_siblings 获取当前 Tag 的下面所有的兄弟节点,返回一个生成器 .previous_element 获取解析过程中上一个被解析的对象(字符串或 tag),可能与 previous_sibling 相同,但通常是不一样的 .next_element 获取解析过程中下一个被解析的对象(字符串或 tag),可能与 next_sibling 相同,但通常是不一样的 .previous_elements 返回一个生成器,可以向前访问文档的解析内容 .next_elements 返回一个生成器,可以向后访问文档的解析内容 .has_attr 判断 Tag 是否包含属性
搜索文档树 find_all(name, attrs, recursive, text, **kwargs) 在上面的栗子中我们简单介绍了 find_all 的使用,接下来介绍一下 find_all 的更多用法-过滤器。这些过滤器贯穿整个搜索 API,过滤器可以被用在 tag 的 name 中,节点的属性等。
(1)name 参数
字符串过滤 :会查找与字符串完全匹配的内容
bs.find_all("a")
正则表达式过滤 :如果传入的是正则表达式,那么 BeautifulSoup4 会通过 search()来匹配内容
bs.find_all(re.compile("a"))
列表 :如果传入一个列表,BeautifulSoup4 将会与列表中的任一元素匹配到的节点返回
bs.find_all(["meta","link"])
方法 :传入一个方法,根据方法来匹配
下例是判断标签里是否含有 name 属性
1 2 3 def name_is_exists (tag ): return tag.has_attr("name" ) t_list = bs.find_all(name_is_exists)
(2)attrs 参数
并不是所有的属性都可以使用上面这种方式进行搜索,比如 HTML 的data-*
属性:
t_list = bs.find_all(data-foo="value")
❌
如果执行这段代码,将会报错 。我们可以使用 attrs 参数,定义一个字典来搜索包含特殊属性的 tag:
t_list = bs.find_all(attrs={"data-foo":"value"})
✔
(3)text 参数
通过 text 参数可以搜索文档中的字符串内容,与 name 参数的可选值一样,text 参数接受 字符串,正则表达式,列表
1 2 3 4 t_list = bs.find_all(attrs={"data-foo" : "value" }) t_list = bs.find_all(text="hao123" ) t_list = bs.find_all(text=["hao123" , "地图" , "贴吧" ]) t_list = bs.find_all(text=re.compile ("\d" ))
当我们搜索 text 中的一些特殊属性时,同样也可以传入一个方法来达到我们的目的:
1 2 3 def length_is_two (text ): return text and len (text) == 2 t_list = bs.find_all(text=length_is_two)
(4)**kwargs 参数
1 2 3 4 5 6 t_list = bs.find_all(id ="head" ) t_list = bs.find_all(href=re.compile ("http://baidu.com" )) t_list = bs.find_all(class_=True )
(5)limit 参数
可以传入一个 limit 参数来限制返回的数量,当搜索出的数据量为 5,而设置了 limit=2 时,此时只会返回前 2 个数据
bs.find_all("a",limit=2)
find_all 除了上面一些常规的写法,还可以对其进行一些简写:
bs.find_all("a")
👉 bs("a")
bs.a.find_all(text="新闻")
👉 bs.a(text="新闻")
find() find()将返回符合条件的第一个 Tag,有时我们只需要或一个 Tag 时,我们就可以用到 find()方法了。当然了,也可以使用 find_all()方法,传入一个 limit=1,然后再取出第一个值也是可以的,不过未免繁琐。
bs.find_all("title",limit=1)
👉 bs.find("title")
从结果可以看出 find_all,尽管传入了 limit=1,但是返回值仍然为一个列表,当我们只需要取一个值时,远不如 find 方法方便。但
是如果未搜索到值时,将返回一个 None。
CSS 选择器 BeautifulSoup 支持发部分的 CSS 选择器,在 Tag 获取 BeautifulSoup 对象的.select()方法中传入字符串参数,即可使用 CSS 选择器的语法找到 Tag:
(1)通过标签名 Tag 查找
bs.select('title')
(2)通过类名 Class 查找
bs.select('.hassan')
(3)通过 id 查找
bs.select('#hassan')
(4)组合查找
bs.select('div .bri')
(5)属性查找
bs.select('a[class="bri"]')
bs.select('a[href="http://baidu.com"]')
(6)直接子标签查找
bs.select("head > title")
(7)兄弟节点标签查找
bs.select(".head ~ .title")
(8)获取内容
1 2 t_list = bs.select("title" ) print(bs.select('title' )[0 ].get_text())
正则提取 正则表达式 :通常被用来检索、替换那些符合某个模式(规则)的文本 。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符及这些特定字符的组合,组成一个“规则字符串” ,这个“规则字符串”用来表达对字符串的一种过滤逻辑 。Python 中使用re 模块 操作正则表达式。
re 模块 Re 库主要功能函数
函数 说明 re.search() 在一个字符串中搜索匹配正则表达式的第一个位置 ,返回 match 对象(左闭右开,首标为 0) re.match() 从一个字符串的开始位置起匹配正则表达式,返回 match 对象 re.findall() 搜索字符串,以列表类型 返回全部能匹配的子串 re.split() 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 re.finditer() 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是 match 对象 re.sub() 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
修饰符 描述 re.l 使匹配对大小写不敏感 re.L 使本地化识别(locale-aware)匹配 re.M 多行匹配,影响^和$ re.S 使"."忽视包括换行在内的所有字符 re.U 根据 Unicode 字符集解析字符。这个标志影响 \w, \W, \b, \B re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解
re 模块下的函数 compile(pattern):创建模式对象
1 2 3 4 5 6 import repat=re.compile ("AA" ) m=pat.search("ABC" ) print(m)
search(pattern,string):在字符串中寻找模式
1 2 3 4 5 6 import rem = re.search("asd" , "ASDasd" ) print(m) m = re.search("asd" , "ASDASD" ) print(m)
match(pattern,string):在字符串开始处匹配模式
1 2 3 4 5 6 7 8 9 10 11 pat=re.compile ("a" ) print(pat.match("Aasd" )) print(pat.match("aASD" )) if pat.search("asd" ):print ("OK" ) if re.search("a" ,"ASD" ):print ("OK" )
split(pattern,string):根据模式分割字符串,返回列表
1 2 3 4 5 6 7 8 9 10 11 12 re.split( , , a,s,d,asd ) ["a" ,"s" ,"d" ,"asd" ] pat = re.compile ( , ) pat.split( a,s,d,asd ) ["a" ,"s" ,"d" ,"asd" ] re.split( [, ]+ ,"a" ,"s" ,d ,,,,,asd ) ["a" ,"s" ,"d" ,"asd" ] re.split( [, ]+ ,"a" ,"s" ,d ,,,,,asd ,maxsplit=2 ) ["a" ,"s" ,"d" ,,,,,asd ] pat = re.compile ( [, ]+ ) pat.split("a" ,"s" ,d ,,,,,asd ,maxsplit=2 ) ["a" ,"s" ,"d" ,,,,,asd ]
findall(pattern,string):列表形式返回匹配项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import reprint(re.findall("a" ,"ASDaDFGAa" )) pat = re.compile ("a" ) print(pat.findall("ASDaDFGAa" )) pat = re.compile ("[A-Z]+" ) print(pat.findall("ASDcDFGAa" )) pat = re.compile ([A-Z]) pat.findall("ASDcDFGAa" ) ["A" ,"S" ,"D" ,"D" ,"F" ,"G" ,"A" ] pat = re.compile ( [A-Za-z] ) pat.findall("ASDcDFGAa" ) ["A" ,"S" ,"D" ,"c" ,"D" ,"F" ,"G" ,"A" ,"a" ]
sub(pat,repl,string) :用 repl 替换 pat 匹配项
(留的是中间的,因为中间在中心)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 re.sub("a" ,"A" ,"abcasd" ) AbcAsd pat = re.compile ("a" ) pat.sub("A" ,"abcasd" ) AbcAsd pat=re.compile (r www.(.*)..{3 } ) 反斜杠。 出来的表达式也更直观 pat.match( www.dxy.com ).group(1 ) dxy re.sub(r www.(.*)..{3 } ,r , hello,www.dxy.com ) pat.sub(r , hello,www.dxy.com ) hello,dxy pat=re.compile (r (w+) (w+) ) s= hello world ! hello hz ! pat.findall( hello world ! hello hz ! ) [( hello , world ), ( hello , hz )] pat.sub(r ,s) 即组1 替换组2 ,组2 替换组1 ,调换位置。 world hello!hz hello!
escape(string) :对字符串里面的特殊字符串进行转义
1 2 re.escape( www.dxy.cn ) www\.dxy\.cn
上面的函数中,只有 match、search 有 group 方法,其他的函数没有。
函数的方法 group:获取子模式(组)的匹配项
1 2 3 4 5 6 7 8 pat = re.compile (r www.(.*).(.*) ) m = pat.match( www.dxy.com ) m.group() www.dxy.com m.group(1 ) dxy m.group(2 ) com
start:给定组匹配项的开始位置
end:给定组匹配项的结束位置
span:给定组匹配项的开始结束位置
正则表达式 元字符 “.” :通配符,除换行符外的任意的 1 个字符
1 2 3 4 5 6 7 8 9 10 11 pat=re.compile ("." ) pat.match("abc" ) <_sre.SRE_Match object at 0xb72b6170 > pat.match("abc" ).group() a pat.search("abc" ).group() a pat.match( ).group() Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: NoneType object has no attribute group
“” : 转义符
1 2 3 4 5 pat=re.compile ("." ) pat.search( abc.efg ).group() . pat.findall( abc.efg ) ["." ]
“[…]” : 字符集合,匹配里面的任意一个元素
1 2 3 4 5 6 7 pat=re.compile ( [abc] ) pat.match("axbycz" ).group() a pat.search("axbycz" ).group() a pat.findall("axbycz" ) ["a" ,"b" ,"c" ]
“d” : 数字
1 2 3 4 5 6 7 8 9 10 >>> pat=re.compile ("d" )>>> pat.search( ax1by2cz3 ).group() >>> 1 >>> pat.match( ax1by2cz3 ).group() 符串头 >>> Traceback (most recent call last):>>> File "<stdin>" , line 1 , in <module>>>> AttributeError: NoneType object has no attribute group>>> pat.findall( ax1by2cz3 ) >>> [ 1 , 2 , 3 ]
“D” : 非数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 >>> pat=re.compile ("D" )>>> pat.match( ax1by2cz3 ).group()>>> a>>> pat.search( ax1by2cz3 ).group()>>> a>>> pat.findall( ax1by2cz3 )>>> ["a" , x ,"b" , y ,"c" , z ]>>> 1 >>> 2 >>> 3 >>> 4 >>> 5 >>> 6 >>> 7 >>> “s” :空白字符 、 、>>> 、>>> pat=re.compile ("D" )>>> pat.match( ax1by2cz3 ).group()>>> a>>> pat.search( ax1by2cz3 ).group()>>> a>>> pat.findall( ax1by2cz3 )>>> ["a" , x ,"b" , y ,"c" , z ]>>> 1 >>> 2 >>> 3 >>> 4 >>> 5 >>> 6 >>> 7 >>> “s” :空白字符 、 、>>> 、
“S” :非空白字符
1 2 3 4 5 >>> pat=re.compile ("S" )>>> pat.search( ax1 by2 cz3 ).group()>>> a>>> pat.findall( ax1 by2 cz3 )>>> ["a" , x , 1 ,"b" , y , 2 ,"c" , z , 3 ]
“w” :单个的 数字和字母,[A-Za-z0-9]
1 2 3 4 5 6 >>> pat=re.compile ( w )>>> pat.search( 1 a2b3c ).group()>>> 1 >>> pat.findall( 1 a2b3c )>>> [ 1 ,"a" , 2 ,"b" , 3 ,"c" ]>>> pat.match( 1 a2b3c ).group()
“W”:非单词字符,除数字和字母外
1 2 3 4 5 >>> pat=re.compile ( W )>>> pat.findall( 1 a2我b3c ) >>> [ æ , ˆ , ‘ ]>>> pat.search( 1 a2我b3c ).group()>>> æ
数量词 “*” :0 次或多次 ( 乘 0 会变成 0)
1 2 3 4 5 6 7 >>> pat = re.compile ( [abc]* )>>> pat.match( abcabcdefabc ).group()>>> abcabc >>> pat.search( abcabcdefabc ).group()>>> abcabc >>> pat.findall( abcabcdefabc )>>> [ abcabc , , , ,"abc" , ]
“+” :1 次或多次
1 2 3 4 5 6 7 >>> pat = re.compile ( [abc]+ )>>> pat.match( abcdefabcabc ).group()>>> abc>>> pat.search( abcdefabcabc ).group()>>> abc>>> pat.findall( abcdefabcabc )>>> ["abc" , abcabc ]
“?” :0 次或 1 次,match,search 不会出现 none,会出现’ ‘ (因为 0 次也是符合的) 0 次或 1 次不是指[xxx]这个集合,而是其中的任何的一个字符
1 2 3 4 5 6 7 >>> pat = re.compile( [abc]? ) >>> pat.match( defabc ).group() >>> pat.match( abcdefabc ).group()>>> a>>> pat.search( defabc ).group() >>> pat.findall( defabc ) >>> [ , , ,"a" ,"b" ,"c" , ]
“数量词?” :非贪婪模式:只匹配最少的(尽可能少);默认贪婪模式:匹配最多的(尽可能多)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> pat = re.compile ( [abc]+ ) >>> pat.match( abcdefabcabc ).group() >>> abc>>> pat.match( bbabcdefabcabc ).group()>>> bbabc>>> pat.search( dbbabcdefabcabc ).group()>>> bbabc>>> pat.findall( abcdefabcabc )>>> ["abc" , abcabc ]>>> pat = re.compile( [abc]+? ) #非贪婪模式:+? >>> pat.match( abcdefabcabc ).group() >>> a>>> pat.search( dbbabcdefabcabc ).group()>>> b>>> pat.findall( abcdefabcabc )>>> ["a" ,"b" ,"c" ,"a" ,"b" ,"c" ,"a" ,"b" ,"c" ]
“{m}” :匹配字符串出现 m 次
1 2 3 4 5 >>> pat = re.compile ( [op]{2 } ) >>> pat.search( abcooapp ).group() >>> oo>>> pat.findall( abcooapp ) >>> [ oo , pp ]
“{m,n}” :匹配字符串出现 m 到 n 次
1 2 3 4 5 6 7 >>> pat = re.compile ( [op]{2 ,4 } ) >>> pat.match( pppabcooapp ).group() >>> ppp>>> pat.search( pppabcooapp ).group() >>> ppp>>> pat.findall( pppabcooapp ) >>> [ ppp , oo , pp ]
.group() #匹配第一次出现 边界 “^” :匹配字符串开头或行头
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 >>> pat = re.compile ( ^[abc] ) >>> pat.search( defabc ).group()>>> pat.match( defabc ).group() >>> pat.findall( defabc )>>> []>>> pat.search( adefabc ).group()>>> a>>> pat.match( adefabc ).group() >>> a>>> pat.findall( adefabc )>>> ["a" ]>>> pat = re.compile ( ^[abc]+ ) 多个 >>> pat.findall( cbadefab )>>> [ cba ]>>> pat = re.compile(r ^[abc]+? ) #开头是a、b、c中的任意一个的一次或则多次,非贪婪:匹 配一个 >>> pat.findall( cbadefab )>>> ["c" ]
“$” :匹配字符串结尾或则行尾
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> pat = re.compile ( [abc]$ )>>> pat.match( adefAbc ).group() None >>> pat.search( adefAbc ).group() >>> c>>> pat.findall( adefAbc )>>> ["c" ]>>> pat = re.compile ( [abc]+$ )>>> pat.search( adefAbc ).group() 多个 >>> bc>>> pat.findall( adefAbc )>>> [ bc ]
“A”:匹配字符串开头
1 2 3 4 5 >>> pat = re.compile ( A[abc]+ )>>> pat.findall( cbadefab )>>> [ cba ]>>> pat.search( cbadefab ).group()>>> cba
“Z”:匹配字符串结尾
1 2 3 4 5 >>> pat = re.compile( [abc]+Z ) >>> pat.search( cbadefab ).group() >>> ab >>> pat.findall( cbadefab ) >>> [ ab ]
分组 (…):分组匹配,从左到右,每遇到一个 ( 编号+1,分组后面可加数量词
1 2 3 4 5 6 7 8 9 10 11 12 >>> pat=re.compile (r (a)w(c) ) >>> pat.match( abcdef ).group()>>> abc>>> pat=re.compile ( (a)b(c) ) >>> pat.match( abcdef ).group() >>> abc>>> pat.match( abcdef ).group(1 ) >>> a>>> pat.match( abcdef ).group(2 ) >>> c>>> pat.match( abcdef ).groups() >>> ("a" ,"c" )
:引用编号为的分组匹配到的字符串
1 2 3 >>> pat=re.compile (r www.(.*)..{3 } )>>> pat.match( www.dxy.com ).group(1 )>>> dxy
“(?P…)” :在模式里面用()来表示分组(命名分组),适用于提取目标字符串中的某一些部位。
1 2 3 4 5 6 7 8 9 10 11 >>> pat=re.compile(r (?P<K>a)w(c) ) #分2组:命名分组+匿名分组 >>> pat.search( abcdef ).groups() >>> ("a" ,"c" )>>> pat.search( abcdef ).group(1 ) >>> a>>> pat.search( abcdef ).group(2 ) >>> c>>> pat.search( abcdef ).group() >>> abc>>> pat.search( abcdef ).groupdict() >>> { K :"a" }
“(?P=name)”:引用别名为的分组匹配到的串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> pat=re.compile(r (?P<K>a)w(c)(?P=K) ) #(?P=K)引用分组1的值,就是a >>> pat.search( abcdef ).group() a >>> Traceback (most recent call last):>>> File "<stdin>" , line 1 , in <module>>>> AttributeError: NoneType object has no attribute group>>> pat.search( abcadef ).group() >>> abca>>> pat.search( abcadef ).groups()>>> ("a" ,"c" )>>> pat.search( abcadef ).group(1 )>>> a>>> pat.search( abcadef ).group(2 )>>> c
“” :引用分组编号匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> pat=re.compile(r (?P<K>a)w(c)(?P=K) ) #引用分组2的值,就是c >>> pat.findall( Aabcadef ) >>> []>>> pat.findall( Aabcacdef ) >>> [("a" ,"c" )]>>> pat.search( Aabcacdef ).groups()>>> ("a" ,"c" )>>> pat.search( Aabcacdef ).group()>>> abcac>>> pat.search( Aabcacdef ).group(1 )>>> a>>> pat.search( Aabcacdef ).group(2 )>>> c
特殊构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 (?:…) (…)不分组版本,用于使用 | 或者后接数量词 (?iLmsux) iLmsux的每个字符代表一个匹配模式,只能用在正则表达式的开头,可选多个 (?#…) #号后的内容将作为注释 (?=…) 之后的字符串内容需要匹配表达式才能成功匹配 (?!…) 之后的字符串不匹配表达式才能成功 (?(?(?(id/name) yes |no) 如果编号为id/名字为name的组匹配到字符串,则需要匹配yes,否则匹配 no,no可以省略 ``` “(?:…)” :()里面有?:表示该()不是分组 ```python >>> pat=re.compile(r a(?:bc) ) >>> pat.findall("abc" )>>> ["abc" ]>>> pat.match("abc" ).groups()
“(?=…)”:匹配…表达式,返回。对后进行匹配,总是对后面进行匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> pat=re.compile(r w(?=d) ) #匹配表达式d,返回数字的前一位,w:单词字符[A-Za-z0-9] >>> pat.findall( abc1 def1 xyz1 )>>> ["c" ,"f" , z ]>>> pat.findall( zhoujy20130628hangzhou ) >>> [ y , 2 , 0 , 1 , 3 , 0 , 6 , 2 ]>>> pat=re.compile(r w+(?=d) ) >>> pat.findall( abc1,def1,xyz1 ) >>> ["abc", def , xyz ] >>> pat.findall( abc21,def31,xyz41 )>>> ["abc2" , def3 , xyz4 ]>>> pat.findall( zhoujy20130628hangzhou )>>> [ zhoujy2013062 ]>>> pat=re.compile(r [A-Za-z]+(?=d) ) #[A-Za-z],匹配字母,可以用其他的正则方法 >>> pat.findall( zhoujy20130628hangzhou123 ) >>> [ zhoujy , hangzhou ]>>> pat.findall( abc21,def31,xyz41 )>>> ["abc", def , xyz ]
“(?!…)” 不匹配…表达式,返回。对后进行匹配
1 2 3 4 5 6 >>> pat=re.compile(r [A-Za-z]+(?!d) ) #[A-Za-z],匹配字母,可以用其他的正则方法 >>> pat.findall( zhoujy20130628hangzhou123,12 ,binjiang310 ) 串,列表返回 >>> [ zhouj , hangzho , binjian ]>>> pat.findall( abc21,def31,xyz41 )>>> [ ab , de , xy ]
“(?<=…)”:匹配…表达式,返回。对前进行匹配,总是对前面进行匹配
1 2 3 4 5 6 7 >>> pat=re.compile(r (?<=d)[A-Za-z]+ ) #匹配前面是数字的字母 >>> pat.findall( abc21,def31,xyz41 )>>> []>>> pat.findall( 1 abc21,2 def31,3 xyz41 )>>> ["abc", def , xyz ] >>> pat.findall( zhoujy20130628hangzhou123,12 ,binjiang310 )>>> [ hangzhou ]
“(?<!…)”:不匹配…表达式,返回。对前进行匹配,总是对前面进行匹配
1 2 3 4 5 >>> pat=re.compile(r (?<!d)[A-Za-z]+ ) #匹配前面不是数字的字母 >>> pat.findall( abc21,def31,xyz41 )>>> ["abc", def , xyz ] >>> pat.findall( zhoujy20130628hangzhou123,12 ,binjiang310 )>>> [ zhoujy , angzhou , binjiang ]
“(?(id/name) yes |no)”: 组是否匹配,匹配返回
1 2 3 4 5 6 7 8 9 10 11 12 >>> pat=re.compile(r a(d)?bc(?(1)d) ) #no省略了,完整的是adbcd ==> a2bc3,总共5位, 第2 位是可有可无的数字,第5 为是数字 >>> pat.findall( abc9 ) >>> [ ]>>> pat.findall( a8bc9 ) >>> [ 8 ]>>> pat.match( a8bc9 ).group()>>> a8bc9>>> pat.match( a8bc9 ).group(1 )>>> 8 >>> pat.findall( a8bc ) >>> []
“(?iLmsux)”:这里就介绍下 i 参数:大小写区分匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> pat=re.compile (r"abc" )>>> pat.findall("abc" )>>> ["abc" ]>>> pat.findall("ABC" )>>> []>>> pat=re.compile(r (?i)abc ) #(?i) 不区分大小写 >>> pat.findall("ABC" )>>> ["ABC" ]>>> pat.findall("abc" )>>> ["abc" ]>>> pat.findall("aBc" )>>> ["aBc" ]>>> pat.findall("aBC" )>>> ["aBC" ]>>> pat=re.compile (r"abc" ,re.I) >>> pat.findall("aBC" )>>> ["aBC" ]>>> pat.findall("abc" )>>> ["abc" ]>>> pat.findall("ABC" )>>> ["ABC" ]
提取数据 使用正则表达式对需要的项进行处理。
保存数据 Excel 表存储 用 python 库xlwt 将抽取的数据写入 Excel 表格
xlwt 模块 简单使用 xlwt
1 2 3 4 5 import xlwt workbook = xlwt.Workbook(encoding='utf-8' ) worksheet = workbook.add_sheet('sheet1' ) worksheet.write(0 , 0 , 'hello' ) workbook.save('students.xls' )
例子:
将九九乘法表显示在表格中,每个单元格 1 个公式
1 2 3 4 5 6 7 workbook = xlwt.Workbook(encoding='utf-8' ) worksheet = workbook.add_sheet('sheet1' ) for i in range (0 ,9 ): for j in range (0 ,i+1 ): worksheet.write(i, j, "%d * %d = %d" %(i+1 ,j+1 ,(i+1 )*(j+1 ))) workbook.save('students.xls' )
数据库存储 可以参考菜鸟教程:
Python 和 SQLite
SQLite 和 SQL 语句
1.引入 sqlite3 库
import sqlite3
2.初始化数据库
demo:
1 2 3 import sqlite3conn = sqlite3.connect('test.db' ) print ("Opened database successfully" )
3.创建数据表
1 2 3 4 5 6 7 8 9 10 11 12 13 import sqlite3conn = sqlite3.connect('test.db' ) print ("Opened database successfully" )c = conn.cursor() c.execute('''"CREATE TABLE COMPANY (ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL);''' )print ("Table created successfully" )conn.commit() conn.close()
4.数据库存储
插入数据:insert 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import sqlite3conn = sqlite3.connect('test.db' ) c = conn.cursor() print ("Opened database successfully" )c.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ VALUES (1, 'Paul', 32, 'California', 20000.00 )" );c.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ VALUES (2, 'Allen', 25, 'Texas', 15000.00 )" );c.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )" );c.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \ VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )" );conn.commit() print ("Records created successfully" )conn.close()
5.数据库查询
select 操作:
下面的 Python 程序显示了如何从前面创建的 COMPANY 表中获取并显示记录:
1 2 3 4 5 6 7 8 9 10 11 12 import sqlite3conn = sqlite3.connect('test.db' ) c = conn.cursor() print ("Opened database successfully" )cursor = c.execute("SELECT id, name, address, salary from COMPANY" ) for row in cursor:print ("ID = " , row[0 ])print ("NAME = " , row[1 ])print ("ADDRESS = " , row[2 ])print ("SALARY = " , row[3 ], "\n" )print("Operation done successfully" ) conn.close()
6.修改操作:update(略)
7.删除操作:delete(略)
技巧合集 将 excel 文件导入 mysql 数据库 参考文章:将 Excel 文件导入到 Navicat for MySQL 数据库方法-百度经验
python3 中 url 不可以包含中文,需要编码 1 2 3 4 5 from urllib import parsekey = '黄王不分' encoded_url = 'https://blog.harriswong.top/' +urllib.parse.quote(key) print(encoded_url)