本文为 Cheatsheet 类型文章,用于记录我在日常编程中的文本处理相关思路。
本文的目录为:
正则是个很奇葩的名字,为什么叫做正则表达式呢,首先是个表达式,其次,这是一种叫做正则 (regular expression, rational expression) 的表达式。
名称为什么叫做 regular 呢,因为它基于 regular language. 而 regular language 是一种 formal language. 得,现在又开始是编译原理相关概念了。为了逃避概念,通过用途来简单定义正则表达式。
简而言之,就是一种用于字符串搜索的模式。或者就是一种领域专用编程语言。
https://en.wikipedia.org/wiki/Regular_expression
# 元字符
. ^ $ * + ? { } [ ] \ | ( )
* # 速记,天上一个星星都没有,0 到多个。
+ # 一加手机..... 1 到多个。
? # 有还是没有 即 0 or 1
*? # 没有疑问就是贪婪,有疑问就是非贪婪
+?
??
{m} # m 份
{m,n} # 优先匹配 a{2,}b 优先匹配 aaaab 中 aaaab
{m,n}? # 优先匹配 a{2,}b 优先匹配 aaaab 中 aab
[] # [a\-z] == [az-]
# 1. [\w] [\S]
# 2. [^5]
# 3. [akm$] 在 [] 中 $ 并不具备元字符特点
PattenA | PattenB
(...) # 捕获 , 引用可以使用、1 , 但是还有一种扩展语法
(?...) # 扩展
# - (?aiLmsux)
# - (?:...) 不捕获
# - (?P<quote>...) 正则内引用 (?P=quote);python 内获取 m.group('quote') ,m.end('quote');re.sub 内 repl 参数为、g<quote> \g<1> \1
re.split('(\W+)', '...words, words...')
# ['', '...', 'words', ', ', 'words', '...', '']
match 为匹配起始字符 / fullmatch 为全部字符 / search 为搜索
m.group(0)
m.group(1, 2)
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'
m.start() # 起始
m.end() # 结尾
# 编译优于不编译
prog = re.compile(pattern)
result = prog.match(string)
re.match(pattern,string)
解析往往伴随着各种各样奇葩的不奇葩的,诡异的不诡异的网页数据抽取,这个过程中,我们常使用两个库来解决问题,一个库叫做 lxml, 另一个库叫做 BeautifulSoup.
beautifulsoup 可是让我们通过直接手动编写遍历 dom 树的方法来快速遍历 dom 树从而获得数据。相比自己写解析器而言,可以算得上非常的节省时间了。
只要能手动遍历 dom 树,基本上所有的数据都是可以获取的。痛点就是手动编写遍历 Dom 树并且完成测试的时间可能长一些。
但是开发效率就比较低了。
举个例子:
<div id="lal">
<span class="item" itemprop="street-address" title="浦东南路八佰伴西面">
地址:浦东南路八佰伴西面
</span>
<div class="item" itemprop="street-address" title="浦东南路">名称:xxxx</div>
</div>
我想要地址属性,如果是 beautifulsoup, 则我们需要先定位到 id 为 lal 的 div 元素。然后获取每个元素的 text 部分,然后使用 if 判断地址属性,然后提取 text.
但是如果用 xpath, 则可以把对元素的简单定位简单判断直接写在 xpath 表达式。
sel.xpath('//div[@id="lal"]/*[contains(text(),"地址")]/text()').extract_first()
# 如果还需要添加筛选名称,则可是使用
sel.xpath('//div[@id="lal"]/*[contains(text(),"名称")]/text()').extract_first()
这样可以极大的提升开发效率。
页面的结构越复杂,则 xpath 带来的开发效率越高。
XPath 是一种通过路径表达式定位 XML 文档内容的语法。
由于内置了大量的表达式函数,可以通过极少的代码完成定位。
有七种节点类型:
有五种节点间关系:
语法 | 描述 | 例子 |
---|---|---|
nodename | 节点名称 | a |
/ | 根节点 | / |
// | 匹配所有 | bookstore//book |
. | 当前节点 | |
… | 父节点 | a/…/a/… |
@ | 属性 | a/@href |
[] | 谓语 | book[1] , book[last()-1] |
func() | 表达式函数 | postion() |
response.xpath("//*[@id=\"landlb_B04_04\"]/span[2]/a[contains(@href,'market')]")
response.xpath("//*[@id=\"landlb_B04_04\"]/span[2]/a[not(@class)]")
response.xpath("//ul/li/b[contains(text(),'什么玩意')]/following-sibling::span/text()")
response.xpath("//div[@class='address']/text()[preceding::span[@class='item' and contains(text(),'地址:')]]")
response.xpath("//ul/li/b[contains(text(),'什么玩意:')]/following-sibling::a/text()")
//*[contains(text(),'ABC')]
# http://stackoverflow.com/questions/3655549/xpath-containstext-some-string-doesnt-work-when-used-with-node-with-more/3655588#3655588
<div class="atag btag" />
//div[contains(@class, 'atag') and contains(@class ,'btag')]
这两个库是 Python 中常用的解析表达式, parsel 依赖于 lxml , 安装完 lxml 后直接安装即可。
众所周知,Mac 的 Homebrew 很方便,每一次遇到需要下载编译的组件的时候,只需要执行 brew install xxx, 很快就可以使用了。
但 homebrew 安装的软件都是最新的,这很容易导致部分软件由于版本更新带来的兼容性问题。
这不,最近在 Mac 上进行开发的时候每次调用初始化 lxml 的时候总是无法进行解析,最后经过排查发现问题是 lxml 在编译的时候使用的 libxml 2.9.4 但是 使用的版本为 2.9.2 , 于是每当我使用 lxml 的时候,就会报错。
不得已,找到 lxml 的 F&Q 部分发现提 issue 之前需要先查看依赖版本。
于是进入 IPython 排查。
import sys
from lxml import etree
print("%-20s: %s" % ('Python', sys.version_info))
print("%-20s: %s" % ('lxml.etree', etree.LXML_VERSION))
print("%-20s: %s" % ('libxml used', etree.LIBXML_VERSION))
print("%-20s: %s" % ('libxml compiled', etree.LIBXML_COMPILED_VERSION))
print("%-20s: %s" % ('libxslt used', etree.LIBXSLT_VERSION))
print("%-20s: %s" % ('libxslt compiled', etree.LIBXSLT_COMPILED_VERSION))
# Python : sys.version_info(major=3, minor=5, micro=1, releaselevel='final', serial=0)
# lxml.etree : (3, 6, 2, 0)
# libxml used : (2, 9, 2)
# libxml compiled : (2, 9, 4) # 注意问题出在这里。
# libxslt used : (1, 1, 28)
# libxslt compiled : (1, 1, 28)
于是使用 pip 强制进行安装升级。
STATIC_DEPS=true pip install -i http://pypi.douban.com/simple/ –trusted-host pypi.douban.com lxml –ignore-installed –no-cache-dir –upgrade -vvv
安装完毕即可。