…
爬虫 Robots 协议 Robots 协议指定了一个网站可以爬取的信息,例如: http://www.taobao.com/robots.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 User-agent: Baiduspider Allow: /article Allow: /oshtml Allow: /ershou Allow: /$ Disallow: /product/ Disallow: / User-Agent: * Disallow: / # 站点信息 Sitemap: ...
使用爬虫技术,需要注意应:
要伪装User-Agent,且需要多个,随机选取
对参数进行URL编码
注意是否是通过AJAX传输数据
保存服务器下发的Cookies
URL Lib 包 urllib.request
,负责读写 url。 urllib.error
,定义错误与异常。 urllib.parse
,url参数的编码解码。 urllib.robotparser
,用于分析robots.txt文件。
简单的读写URL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from urllib.request import urlopen, Request # 直接打开一个url,Request对象,返回HTTPResponse对象,用法类似文件 res = urlopen(url, data) # 构造Request req = Request(url, headers={ 'User-agent': 'user agent' }) # 或 req.add_header('', '') res = urlopen(req) # 查看结果 res.closed # 查看是否关闭,False with res: res.status # 状态码 res.reason # 状态 res.getrul() # 真正的URL,例如重定向后的URL res.info() # headers res.read() # 返回读取的内容 res.closed # 查看是否关闭,True
URL参数的编码解码:
1 2 3 4 5 6 7 8 9 10 11 12 13 from urllib.request import parse d = { 'id': 1, 'name': 'auther', } # url编码 arg = parse.urlencode(d) # url解码 d = parse.unquote(arg) # 使用 url = f"http://...?{arg}" # GET # 或 urlopen(req, arg.encode()) POST
HTTP 实验网站
AJAX 与 HTTPS 在Chrome浏览器里,进入开发者选项,选择XHR分类,查看异步请求。利用其中的AJAX接口进行数据请求。
HTTPS是由权威机构颁发的证书,颁发的证书文件需要事先上传至被认证的服务器上。当用户访问网站时,用户浏览器会首先得到该网站的服务器证书,用户拿到证书后进行验证,进而判断通信是否安全。
在爬虫中,我们会遇到拥有HTTPS但是不信任的网站,因此要尽量忽略HTTPS以减少工作量。
使用SSL模块忽略HTTPS:
1 2 3 4 5 6 import ssl # 忽略不信任的证书 context = ssl._create_unverified_context() with urlopen(req, context = context) as res: pass
urllib 3 urllib 3库是一个第三方库,提供了例如连接池管理等功能。
使用:
1 2 3 4 5 6 7 8 9 10 import urllib3 # 打开一个 URL 池管理器 with urllib3.PoolManager() as http: # http.urlopen() resp = http.request() # resp.status # resp.reason # resp.headers # resp.data
requests 库 requests库是基于urllib3库的,而且提供了更加友好的API使用。
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 import requests resp = requests.request('GET', url, headers={ 'User-Agent': ua } ) with resp: # resp.url # resp.status_code # resp.request.headers # resp.text # resp.cookies
使用带Cookie的访问:
1 2 3 4 5 with request.Session() as session: for url in urls: resp = session.get(url, headers={'',''}) with resp: pass
XPATH 技术 XPath是用来在XML中查找信息的语言。
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="ISO-8859-1"?> <bookstore> <book> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
在XPath中定义了节点:
元素:<title lang="en">Harry Potter</title>
属性:lang="en"
文本:
命名空间:<?xml version="1.0" encoding="ISO-8859-1"?>
处理指令
注释
文档节点:<bookstore>
也包含节点关系,如:父,子,兄弟,所有祖先,所有后代。
在XPath中,节点之间的父子关系可以用表达式表示:
nodename
:选取此节点的所有子节点
/
:根节点或分隔符
//
:后继节点,不考虑路径
.
:当前节点
..
:父节点
@
:属性
|
:选取多个路径
谓语是用于按照索引选择子节点的工具:
[1]
:第一个元素
[last()]
:最后一个元素
[position()<3]
:前两个元素
[@lang]
:拥有lang属性的元素
[@lang='eng']
:满足条件的元素
[price>10]
:元素值大于10的元素
通配符:
*
:任何元素节点
@*
:任何属性节点
node()
:任何类型节点
轴可定义相对于当前节点的节点集:
ancestor:选取当前节点的所有祖先(父、祖父等)。
ancestor-or-self:选取当前节点的所有祖先(父、祖父等)以及当前节点本身。
attribute:选取当前节点的所有属性。
child:选取当前节点的所有子元素。
descendant:选取当前节点的所有后代元素(子、孙等)。
descendant-or-self:选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following:选取文档中当前节点的结束标签之后的所有节点。
namespace:选取当前节点的所有命名空间节点。
parent:选取当前节点的父节点。
preceding:选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling:选取当前节点之前的所有同级节点。
self:选取当前节点。
使用轴可以选取某些节点:
child::book:选取所有属于当前节点的子元素的 book 节点。
child::text():选取当前节点的所有文本子节点。
安装 lxml 模块:
1 2 3 4 # linux 需要依赖 sudo apt-get install libxml2-dev libxslt-dev # windows 不需要 pip install lxml
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from lxml import etree # 构建标签 root = etree.Element('html') body = etree.Element('body') root.append(body) # 打印HTML print(etree.tostring(root)) print(etree.tostring( root, pretty_print=True ).decode()) # 添加子元素 sub = etree.SubElement(body, 'child1') sub = etree.SubElement(body, 'child2') # 解析HTML etree.HTML(text) a_node.xpath('xpath 路径')
在Chrome使用XPath工具:在选定的标签上右键->Copy->XPath,并根据给定的内容进行修改。也可以使用插件ChroPath
调试。
在分析选定标签的时候,可以优先找id属性,其次class属性。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from lxml import etree import requests url = 'https://moive.douban.com' ua = '' with requests.get( url, headers={'User-Agent': ua} ) as response: # HTML 内容 content = response.txt # 解析为 DOM html = etree.HTML(content) # 使用XPath得到内容 titles = html.xpath("//div[@class='villboard-bd']//tr/td/a/text()") for item in titles: print(item)
XPath 语言
Spider Web Django Flask Cherrypy Tornado 官方文档 Github
优势:
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
分布式系统 Celery 特点:
工作:
需要消息中间件:RabbitMQ 或 Redis
基本使用 1 2 3 4 5 6 7 tasks.py from celery import Celery app = Celery("task_name", backend="redis://local:6379/2", broker="redis://local:6379/1") @app.task # 变为异步 def add(x, y): pass
启动 worker
1 celery worker -A tasks -l INFO
使用
1 2 from tasks import add x = add.delay(1,2)
配置文件 目录:
celery_app
celeryconfig.app
task1.py
task2.py
app.py
1 2 app = Celery("task_name") app.config_from_object("celery_app.celeryconfig")
celeryconfig.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 BROKER_URL = '' CELERY_RESULT_BACKEND = '' # 结果存储到数据库 CELERY_TIMEZONE = '' CELERY_IMPORTS = ( 'celery_app.task1', 'celery_app.task2', ) CELERY_SCHEDULE = { 'task1': { 'task': 'celery_app.task1.add', 'schedule': timedelta(seconds=10), 'args': (2, 3), } }
结合 Django 监控 Flower 进程管理 Supervisor 模板 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 def make_app(): return tornado.web.Application( # 路由配置 [ (r"/", MainHandler), ], # 渲染模板路径 template_path = os.path.join( os.path.dirname(__file__), "template" ), # 开启 Debug debug = True )
控制器
1 2 3 class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", msg="")
index.html
异步服务器与客户端 1 2 3 4 app = tornado.web.Application() http_server = tornado.httpserver.HTTPServer(app) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
接口测试