基本组成
爬虫通常分为数据采集(网页下载)、数据处理(网页解析)和数据存储(有用的信息持久化)三个部分。
工作流程:
设定抓取目标(种子页面/起始页面)并获取网页。
当服务器无法访问时,按照指定的重试次数尝试重新下载页面。
在需要的时候设置用户代理或隐藏真实IP,否则可能无法访问页面。
对获取的页面进行必要的解码操作然后抓取出需要的信息。
在获取的页面中通过某种方式(如正则表达式)抽取出页面中的链接信息。
对链接进行进一步的处理(获取页面并重复上面的动作)。
将有用的信息进行持久化以备后续的处理。
解析工具
urllib
urllib.request
中的 Request urlopen
页面内容抓取工具
正则、lxml、bs4(BeautifulSoup)
lxml
etree.HTML(): 解析HTML对象
etree = etree.HTML(html) etree.xpath()
xpath 语法
表达式 | 作用 |
---|---|
/ | 根节点选取 |
// | 在当前选择的文档中选取 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
beautifulSoup
安装包
pip install beautifulsoup4
导入包
form bs4 import BeautifulSoup
1. 简单使用
beautifulSoup得到的是一个 bsObj 我们可以在它的基础上进行获取我们需要的信息
from urllib.request import urlopen from bs4 import BeautifulSoup html = urlopen('') bsObj = BeautifulSoup(html.read()) print(bsObj.h1) # 获取 h1 标签 # 获取 标签中的 class='green'的 span span = bsObj.findAll('span', {'class': 'green'}) # 获取文本信息 span.get_text() # 获取属性值 span.attrs.get('attr') span.get('attr')
在获取文本信息时, 会将所有的标签信息都清除, 应该尽量避免这样, 尽量保存 HTML 文档的标签结构
find() 和 findAll()
findAll(tag, attrinutsd, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, kewords)
参数解释
tag
- 可以传入一个或者多个标签名称组成的列表来查找, 将返回所有的包含要查找的标签的列表集合attributes
- 传入一个字典格式的属性和属性对应的值{'class', 'green'}
recursive
- 递归参数, 是一个布尔变量, 默认为True
, 会查找需要的子标签以及子标签的子标签,False
只会查找文档的一级标签limit
- 只用于findAll
方法, 获取需要的前几项, 一般是按照页面的顺序来排序的keyword
- 指定关键字, 可以更加具体的指定属性的标签, 注意使用class
是会造成冲突bsObj.findAll(id='text')
bsObj.findAll('', {'id', 'text'})
可以直接获取 bsObj 对象的标签
bsObj.div.h1
获取 h1 标签 - 只对 bsObj 有效
NavigableString
对象用来表示标签里的文字
Comment
对象用来表示文档中的注释
2. 拿取元素
bsObj.div.findAll('img')
找出文档中的第一个 div 然后获取这个 div 中所有的后代中的 img 标签 列表
获取子标签
# 获得表格中的所有的 子标签 (tr th td) 的内容 for child in bsObj.find('table', {'id', 'goo'}).children: print(child)
兄弟标签
bsObj.next_siblings
下一个兄弟标签
bsObj.previous_silblings
上一个兄弟标签
父标签
bsObj.parent
父标签
正则表达式 + BeautiflSoup
[^a-z]
表示不匹配包含在[ ]
中的内容, 例为 不匹配a-z
?!
排除 也表示 不包含, 通常放在 字符或者 正则表达式的前面, 让后面的正则表达式 排除不包含的内容 例^(?![A-Z]).)*$
- 匹配.
但不包含A-Z
import re from bs4 import BeautifulSoup ... bsObj = BeautifulSoup(html) images = bsObj.findAll("img", {"src", re.compile("\.\.\/img\/gifts/img.*\.jpg")}) for image in images: print(iamge['src'])
获取属性
bsObj.attrs
获取全部属性, 得到的是一个字典
bsObj.attrs['src']
获取 src 属性
Lambda 表达式
soup.findAll(lambda tag: len(tag.attrs) == 2)
获取满足 属性的个数是 2 的标签
BeautifulSoup的解析器
Python标准
使用方法: BeautifulSoup(html_doc,"html.parser") 优势:Python内置,执行速度适中,文档容错能力强 劣势:Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 123456
lxml解析器(推荐使用)
使用方法:BeautifulSoup(html_doc,'lxml') 优势:速度快,文档容错能力强(C编写),推荐使用 1234
html5lib
使用方法:BeautifulSoup(html_doc,"html5lib") 优势:最好的容错性,已浏览器的方式解析文档,生成Html5格式的文档 劣势:速度慢,不依赖外部扩展
存储数据
mongodb
常用方法
一般来说,我们创建集合用db.createCollection(name),如:db.createCollection("log"),创建一个名字为log的集合,没有任何的大小,数量限制,使用_id作为默认索引;
限制集合空间的大小:db.createCollection("log",{size:1024})或db.createCollection("log",{capped:true,size:1024}),创建一个名字为log集合,限制它的空间大小为1M,如果超过1M的大小,则会删除最早的记录;
限制集合的最大条数:db.createCollection("log",{max:1024}),创建一个名字为log集合,最大条数为1024条,超过1024再插入数据的话会删除最早的一条记录。这个不能使用capped:true,否则会报错;
即限制最大条数有限制使用空间大小:db.createCollection("log",{size:1024,max:1024})或db.createCollection("log",{capped:true,size:1024,max:1024}),限制集合最大使用空间为1M,最大条数为1024条。
增删改查, 筛选
csv
csvFile = open('editor.csv', 'wt', newline='', encoding='utf-8') writer = csv.writer(csvFile) writer.writerow(content)
每一行都用一个换行符分隔
列与列之间用逗号分隔(逗号分隔值)
图片
from urllib.request import urlretrieve urlretrieve(url, 'pic.jpg') # 保存文件 默认保存在当前文件夹, 也可以写绝对路径
多线程并发下载
多线程的使用
aiohttp
实现并发http请求
import aiohttp async with aiohttp.ClientSession() as session: session get post
asyncio
asyncio的编程模式就是一个消息循环,我们从asyncio模块中直接获取一个 EventLoopd的引用, 然后把需要执行的协程扔到 EventLoop 中执行, 就实现了异步 IO
from asyncio import loop = asyncio.get_event_loop() task = asyncio.wait([self.get_html()]) loop.run_until_complete(task) # 执行函数 loop.close()
消息队列-RobbitMQ (了解)
消息队列(Message Queue)是一种应用之间的通讯方式, 消息发送后可以立即返回, 由消息系统来确保消息的可靠传递。消息的发布者只需要将消息发布到 MQ 之中就行, 不用管是谁来获取消息, 消息的使用者值需要从 MQ 中拿取消息而不用在乎是谁发布符消息。这样发布者和使用者都不用知道对方是谁, 这样就可以将 两者之间 解耦合。
自动化测试工具 selenium