项目首先需要获取豆列中的特定信息, 如内容的链接, 书名等. 考虑到后续处理难度, 链接是唯一的更易操作, 在原型中希望抓取链接. 起初试图使用著名的 Scrapy, 但发现无从下手.
大妈给出了 42 分钟建议
: 42 分钟无法了解并写出可用原型, 说明不适合当下的自己.
经过另一番搜索, 发现 py 自带的 urllib2 似乎已经足可实现原型功能. 如何使用这个库抓取信息呢? 最简使用方法: (py 2.7.9)
# import urllib2
response = urllib2.urlopen(url_link) #抓取link的网页信息(纯文本)
print response.read() #把网页信息打印出来
urllib 还可实现对网页的访问(request)等功能.
ref: py doc urllib
抓取到网页信息之后, 下一步是从网页中提取指定信息. 这个步骤主要是进行大量的字符串信息匹配, 正则表达式是字符串匹配的基本工具.
正则表达式是什么呢? 一种表达世间万物的方式(大雾)...更具体地说, 程序员为了对大量字符串进行匹配, 比如今天匹配 apple, 明天匹配 orange, 后天匹配 apple 后面的 orange...单词无穷无尽, 实在是太麻烦了. 于是他们设计了一套复杂的匹配系统, 用各种匹配符来简化匹配操作.
python 中自带正则表达式模块 re. 常用函数 re.match
, re.search
, re.findall
re.MatchObject
基本实例:
1 basic example
import re
print re.search('apple', 'apple')
print re.search('apple', 'orange')
match = re.search('apple', 'apple')
if match:
print match.group()
<_sre.SRE_Match object at 0x105d87308>
None
apple
2 re.search vs re.match
import re
print re.search('apple', '1apple') #re.search 从字符串中查找, 任意位置开始都可以
print re.match('apple', '1apple') #re.match 只从字符串开始匹配
<_sre.SRE_Match object at 0x105d87238>
None
3 re.findall
print re.findall('a', 'abdcai') #re.findall 返回一个列表
['a', 'a']
4 最基本的通配符
. (默认情况下)匹配除换行符 \n 以外的任意其它字符
\d 匹配数字
* 贪婪匹配零次或多次
? 吝啬匹配
| 逻辑或
[] 用于表示一组字符, 如 \d = [0-9]
print re.findall('ap..e', 'apple aplle apllo')
print re.findall('ap.*', 'apple aplle apllo') # *是贪婪匹配, 会直接匹配整行
print re.findall('ap.*?', 'apple aplle apllo') # 加上?之后则最少匹配了
print re.findall('ap.*? ', 'apple aplle apllo') # 加上?和 空格
['apple', 'aplle']
['apple aplle apllo']
['ap', 'ap', 'ap']
['apple ', 'aplle ']
5
# import urllib2
response = urllib2.urlopen('http://www.douban.com/doulist/14090587/') # 抓取豆列的网页信息(测试中发现豆列的书籍内容都存放在网页)
s = re.findall('http://book.douban.com/subject/[0-9]*/', response.read()) # 用正则表达式匹配字符串, 找到豆列中的书籍链接
s = re.search('http://www.douban.com/doulist/[0-9]*/', input_url)
匹配/一串数字/
re.search('http://book.douban.com/people/.*?/do|http://book.douban.com/people/.*?/wish|http://book.douban.com/people/.*?/collect', doulist_url)
用吝啬匹配用户名, 用逻辑或判断豆瓣想读格式 xxx/do|wish|collect
i_book_num = re.search('\d+', i)
在 i 中寻找数字(书籍链接中的书号), 后续用来访问豆瓣 api
前导串, 可用于在提取同时去掉不需要的前导部分.(example in py doc)
>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
>>> m = re.search('(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
一个大坑, 和 findall 导出的对象格式和中文编码有关, 见 unicode 和中文编码章节
字符串处理是获取特定信息的基本手段. 在其它个人项目中也反复锤炼了基本技能...
1 flags: re.S 多行模式, 可匹配 \n 的多行输入
print re.findall('ap..e', 'app\ne aplle apllo')
print re.findall('ap..e', 'app\ne aplle apllo', flags=re.S)
['aplle']
['app\ne', 'aplle']
2 re.split(pattern, string, maxsplit=0, flags=0)
: 用特定 pattern 分隔 string
3 提取特定字符串信息, 前导串可用于简单加工
i = 'player 162 action fold\nplayer 162 action check\n'
actions = re.findall('player 162 action .*?\n', i, flags=re.S)
print actions
actions = re.findall('(?<=player 162 action ).*?\n', i, flags=re.S)
print actions
['player 162 action fold\n', 'player 162 action check\n']
['fold\n', 'check\n']
这里的正则表达式都只是最简单的应用, 未用到复杂的匹配模式, 但原理是相通的. 正则表达式的语法相对来说比较晦涩, 因此, 这里希望通过相对较多的例子, 唤醒自己快速从已有例子修改, 形成可用的正则表达式.
References: