• 爬虫基础


    爬虫的相关的库

    urllib,requests,selenium,appium
    

    数据解析

    re,xpath,bs4,json
    

    数据存储

    pymysql,mongodb,...
    

    任务

    多线程,协程,异步
    

    框架

    scrapy,scrapy-redis
    

    爬虫库urllib介绍

    安装

    	pip install urllib
    

    请求

    	from urllib.request import urlopen
    
    # 发起网络请求
    resp = urllopen('http://www.hao123.com')
    assert resp.code == 200
    print('请求成功')
    # 保存请求的网页
    # f 变量接收open()函数返回的对象的__enter__()返回结果
    with open('a.html', 'wb') as f:
    	 f.write(resp.read())
    

    urlopen(url, data=None)可以直接发起url的请求, 如果data不为空时,则默认是POST请求,反之为GET请求。
    resp是http.client.HTTPResponse类对象。

    有请求头

    from urllib.request import Request
    
    def search_baidu():
    	# 网络资源的接口(URL)
    	url = 'https://www.baidu.com'
    
    	# 生成请求对象,封装请求的url和头header
    	request = Request(url,headers={'cookie':'放浏览器的cookie'}
    response = urlopen(request)  # 发起请求
    
    assert response.code == 200
    print('请求成功')
    
    # 读取响应的数据
    bytes_ = response.read()
    
    # 将响应的数据写入文件中
    with open('index.html', 'wb') as file:
        file.write(bytes_)
    

    5.2 urllib.parse模块

    此模块有两个核心的函数:

    - quote() 仅对中文字符串进行url编码;
    - urlencode() 可以针对一个字典中所有的values进行编码,然后转成key=value&key=value的字符串。
    	"""
    	复杂的GET请求,多页面请求下载
    	"""
    	from urllib.request import Request, urlopen
    	from urllib.parse import urlencode
    
    	import ssl
    
    	import time
    
    	ssl._create_default_https_context = ssl._create_unverified_context
    
    	url = 'https://www.baidu.com/s?'
    	headers = {'User-Agent':'...'}
    
    	params = {
    		'wd': '',
    		'pn': 0  # 0, 10, 20, 30 ...  = (n-1)*10
    	}
    
    	def pages_get(wd):
    		params['wd'] = wd
    		for page in range(1, 101):
    			params['pn'] = (page-1)*10
    
    			page_url = url+urlencode(params)
    			resp = urlopen(Request(page_url,
    								   headers=headers))
    
    			assert resp.code == 200
    			file_name = 'baidu_pages/%s-%s.html' % (wd, page)
    			with open(file_name, 'wb') as f:
    				bytes_ = resp.read()
    				f.write(bytes_)
    				print(f'{file_name} 写入成功!')
    				time.sleep(0.5)
    
    		print('下载 %s 100页成功!' % wd)
    
    
    	if __name__ == '__main__':
    		pages_get('Python3.6')
    

    5.3 HTTP处理器

    urllib的请求处理器,主要用于urllib.request.build_opener()函数参数,表示构造一个由不同处理组成的伪浏览器。

    5.3.1 HTTPHandler

    处理Http协议的请求处理。

    5.3.2 HTTPCookieProcessor

    处理Cookie的处理器,创建类实例时,需要提供http.cookiejar.CookieJar类的实例对象。

    requests库介绍

    安装

    pip install requests -i https://mirrors.aliyun.com/pypi/simple
    

    requests核心函数

    requests.request()
    method:str制定请求方法,GET,POST,PUT,DELETE
    url:str 请求资源接口
    params:dict,请求GET请求查询参数
    data:dict POST,PUT,DELETE请求表单参数
    json:dict用于上传json数据参数,封装到body
    files:dict
    	{'filename',file-like-object}
    	{'filename',file-like-object,content-type}
    	{'filename','file-like-object',content-type,custom-headers}
    指定files上传文件,用post请求,请求头Content-Type
    headers/cookies:dict 请求头
    proxies:dict代理
    auth:tuple 用于授权用户授权用户名口令
    requests.get() 发起GET请求,查询数据,可以用的参数,url,params,json,headers/cookies/auth
    requests.post() 参数,url,data/files/json/headers/cookies/auth
    requests.put() 发起put请求,修改更新数据
    requests.patch()
    requests.delete() 删除数据
    Response
    status_code 响应状态码 一般是200,url,headers,cookies,text,content,encoding,
    	json(), 响应数据类型,application/json,将响应数据进行反序列化,JSON.stringify(obj)序列化,JSON.parse(text),反序列化
    

    xpath解析数据介绍

    绝对路径,从根标签,依次向下查找
    相对路径
    	//相对于整个文档
    	/依次查找
    	.//相对于当前节点向下找所有节点
    	./相对于当前节点
    	//title/text() 查找文本
    	//img/@href 提取属性
    	//meta[1]/@content第一个
    	//meta[lats()]/@content 最后一个
    	//meta[position()-2]/@content倒数第2个
    	//meta[position()<3]//@content 前两个
    	//img[@class="circle-img"] 根据属性条件查找标签
    	//title/text()|//img@src 同时提取两个
    	 //div[contains(@class,'page')] 查找class包含page的所有的div标签
    	 //div[end-with(@class,"clearfix")]最后一个class属性值为clearfix的div标签
    

    扩展封装的ES-SDK

    """
    基于requests库封装操作ElasticSearch搜索引擎的函数 (SDK)
    """
    from urllib.parse import quote
    
    import requests
    
    INDEX_HOST = '119.3.170.97'
    INDEX_PORT = 80
    
    
    class ESIndex():
    	"""ES的索引库的类"""
    
    	def __init__(self, index_name, doc_type):
    		self.index_name = index_name
    		self.doc_type = doc_type
    
    	def create(self):  # 创建索引库
    		url = f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}'
    		json_data = {
    			"settings": {
    				"number_of_shards": 5,
    				"number_of_replicas": 1
    			}
    		}
    		resp = requests.put(url, json=json_data)
    		if resp.status_code == 200:
    			print('创建索引成功')
    			print(resp.json())
    
    	def delete(self):  # 删除索引库
    		resp = requests.delete(f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}')
    		if resp.status_code == 200:
    			print('delete index ok')
    
    	def add_doc(self, item: dict):
    		# 向库中增加文档
    		doc_id = item.pop('id', None)
    		url = f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}/{self.doc_type}/'
    		if doc_id:
    			url += str(doc_id)
    
    		resp = requests.post(url, json=item)
    		if resp.status_code == 200:
    			print(f'{url} 文档增加成功!')
    
    	def remove_doc(self, doc_id):
    		# 删除文档
    		url = f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}/{self.doc_type}/{doc_id}'
    		resp = requests.delete(url)
    		if resp.status_code == 200:
    			print(f'delete {url} ok')
    
    	def update_doc(self, item: dict):
    		# 更新文档
    		doc_id = item.pop('id')
    		url = f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}/{self.doc_type}/{doc_id}'
    		resp = requests.put(url, json=item)
    		assert resp.status_code == 200
    		print(f'{url} update ok')
    
    	def query(self, wd=None):
    		# 查询
    		q = quote(wd) if wd else ''
    		url = f'http://{INDEX_HOST}:{INDEX_PORT}/{self.index_name}/_search?size=100'
    		if q:
    			url += f'&q={q}'
    		resp = requests.get(url)
    		datas = []
    		if resp.status_code == 200:
    			ret = resp.json()
    			hits = ret['hits']['hits']
    			if hits:
    				for item in hits:
    					data = item['_source']
    					data['id'] = item['_id']
    
    					datas.append(data)
    
    		return datas
    
    
    if __name__ == '__main__':
    	index = ESIndex('gushiwen', 'tuijian')
    	# index.create()
    	# index.add_doc({
    	#     'id': 1,
    	#     'name': 'disen',
    	#     'price': 19.5
    	# })
    	#
    	# index.add_doc({
    	#     'id': 2,
    	#     'name': 'jack',
    	#     'price': 10.5
    	# })
    
    	print(index.query())
    

    扩展linuk文件权限

    解析站长之家

    """
    基于正则re模块解析数据
    """
    import re
    import os
    
    import requests
    
    from utils.header import get_ua
    
    base_url = 'http://sc.chinaz.com/tupian/'
    url = f'{base_url}shuaigetupian.html'
    
    headers = {
    	'User-Agent': get_ua()
    }
    
    if os.path.exists('mn.html'):
    	with open('mn.html', encoding='utf-8') as f:
    		html = f.read()
    else:
    	resp = requests.get(url, headers=headers)
    	print(resp.encoding)  # IOS-8859-1
    	resp.encoding = 'utf-8'  # 可以修改响应的状态码
    	assert resp.status_code == 200
    	html = resp.text
    	with open('mn.html', 'w', encoding=resp.encoding) as f:
    		f.write(html)
    
    # print(html)
    #  [u4e00-u9fa5]
    compile = re.compile(r'<img src2="(.*?)" alt="(.*?)">')
    compile2 = re.compile(r'<img alt="(.*?)" src="(.*?)">')
    
    imgs = compile.findall(html)  # 返回list
    if len(imgs) == 0:
    	imgs = compile2.findall(html)
    
    print(len(imgs), imgs, sep='
    ')
    
    # 下一页
    next_url = re.findall(r'<b>20</b></a><a href="(.*?)" class="nextpage"',html, re.S)
    
    print(base_url+next_url[0])
    

    re正则

    字符表示

    	. 表示任意一个字符
    	[a-z],[A-Z],[0-9] 小写,大写,数字
    	[|sS]匹配空白字符 换行S非空白,不包括换行
    	w数字字母下划线
    	f
    

    特殊字符

    ^ 开始
    $ 结尾
    *匹配零次或多次
    ?非贪婪字符 
    {n} 匹配确定的n次
    {n,} 至少匹配n次
    {n,m}最少n最多m次
    贪婪 <.*>
    非贪婪 <.*?>
    

    分组表示

    ()普通分组 多个正则分组 search().groups 返回是元祖
    `(?P<name> 字符+数量)`带有名称的分组, 多个正则分组时,search().groupdict()返回是字典, 字典的key即是分组名。
    	import re
    
    text = '123abc90ccc'
    re.search(r'(?P<n1>d+?)[a-z]+?(?P<n2>d+)', text).groupdict()
    

    python 中正则模块

    re.compile() 一次生成正则对象,可以匹配查询
    re.match(),re.search(),re.findall(),re.sub()
    	re.sub('d+','120',text)
    re.split()
    

    进程线程

    multiprocessing模块
    	Process进程类
    	Queue进程间通信的队列
    		put(item,timeout)
    		item=get(timeout)
    		threading模块(线程)
    			Thread线程类
    			线程间通信(访问对象)
    			queue.Queue线程队列
    			回调函数(主线程声明,子线程调用函数)
    

    bs4数据解析

    安装

    pip install bs4
    from bs4 import BeautifulSoup
    root = BeautifuleSoup(html,'lxml')
    查找节点(bs4.element,Tag)
    root.find('标签名',class="",id="") 返回Tag对象
    root.find_all('标签名',id="",limit=3) 返回limit指定数量的Tag对象
    root.select('样式选择器')
    	#id,.class,标签名,'属性',div ul 间接子节点 div>ul 直接子节点
    	节点属性,获取文本数据div.text/div.string/div.get_text()
    	获取属性 div.get('属性名')
    		div['属性名']
    		div.attrs['属性名']
    		div.attrs.get('属性名')
    		contents 获取所有文本子节点
    		descendants 获取所有子节点的对象
    

    协程爬虫

    协程是线程的替代,线程是cpu调度,协程是用户调度的
    

    协程的方式

    基于生成器generator(过渡)
    	yield,send()
    	- Python3 之后引入了 asyncio模块
      - @asyncio.coroutine 协程装饰器, 可以在函数上使用此装饰器,使得函数变成协程对象
      - 在协程函数中,可以使用yield from 阻塞当前的协程,将执行的权限移交给 yield from 之后的协程对	象。
      - asyncio.get_event_loop() 获取事件循环模型对象, 等待所有的协程对象完成之后结束。
    - Python3.5之后,引入两个关键字
      - async 替代  @asyncio.coroutine
      - await 替代  yield from 
    协程的知识点
    
    - @asyncio.coroutine 将函数升级为协程对象(闭包函数)
    
    - yield from 将执行的权限移交给其它协程对象
    
    - loop = asyncio.get_event_loop()  获取事件模型
    
    - loop.run_until_complete(协程对象) 事件模型启动协程,直到协程执行完成后,释放事件模型对象。
    
      - 如果是多个协程对象时, 需要使用asyncio.wait() 将多个协程对象以元组方式传入到wait()方法中。
    
    - Python 3.5+增加两个关键字
    
      - async 替代@asyncio.coroutine
      - await 替代 yield from 
    
      注意: async 和 await 必须同时使用
    

    seleinum 库

    安装

    pip install seleinum
    

    下载驱动

    Chrome驱动
    seleinum.webdriver.common.by.By 导入By
    	By.CLASS_NAME
    	By.CSS_SELECTOR
    	By.ID
    	By.NAME
    	By.XPATH
    	By.LINK_TEXT
    seleinum.webdriver.Chrome
    	chrome.get(url)
    	chrome.find_element(by,value) 在element 加s 查找多个节点
    	chrome.window_handlers:list 获取窗口标签页
    	chrome.excute_script(js) 当前窗口执行js脚本
    	chrome.swich_to.window/frame() 切换窗口
    	chrome.close()
    	chrome.page_source 渲染之后的html 页面代码
    WebElement 查找元素对象类型
    	click() 点击
    	send_keys()输入内容
    	等待WebElement元素出现
    	selenium.webdriver.support模块
    		ui WebDriverWait(driver,timeout)
    			until(expected_conditions,err_msg)
    		expected_conditions
    			visibility_of_all_element_located((By,value))
    

    docker 教程

    docker 容器,将远程docker拉到本地
    

    基本操作

    - 镜像操作
    	- 基本信息 
    		- 名称
    		- 版本
    		- ID
    		- 描述
    	- docker images 查看所有镜像
    	- docker rmi 名称:版本号 / ID 删除镜像
    	- docker run 名称:版本号 / ID 启动镜像
    		- -dit 后台启动镜像,启动后可进入容器并打开新的terminal(终端)
    		- -p 宿主机端口: 容器端口
    - 容器操作
    	- docker ps 查看正运行的容器
    		- -a 查看所有的容器
    		- -l 查看最后一个启动的容器
    
    	- docker logs 容器名或ID  查看容器运行的日志
    	- docker exec 容器名或ID Linux命令 在容器中执行Linux命令
    		- docker exec -it 容器名或ID bash 进入容器
    	- docker stop 容器名或ID
    	- docker start 容器名或ID
    	- docker restart 容器名或ID
    	- docker rm -f 容器名或ID 删除容器, -f强制删除正运行的容器
    

    日志模块

    python中的logging
    级别
    DEBUG 最轻
    INFO
    WARNING
    ERROR
    CRITICAL 最重
    

    输出到控制台
    使用 logging 在控制台打印日志,这里我们用 Pycharm 编辑器来观察:
    保存为.txt文件
    依旧是强大的 basicConfig

    logging.basicConfig(level=logging.DEBUG, filename='coder.log', filemode='a')   # 配置中填写 filename (指定文件名) 和 filemode (文件写入方式)
    

    image

    import os
    import logging
    import uuid
    from logging import Handler, FileHandler, StreamHandler
    ​
    ​
    class PathFileHandler(FileHandler):
    	def __init__(self, path, filename, mode='a', encoding=None, delay=False):
    ​
    		filename = os.fspath(filename)
    		if not os.path.exists(path):
    			os.mkdir(path)
    		self.baseFilename = os.path.join(path, filename)
    		self.mode = mode
    		self.encoding = encoding
    		self.delay = delay
    		if delay:
    			Handler.__init__(self)
    			self.stream = None
    		else:
    			StreamHandler.__init__(self, self._open())
    ​
    ​
    class Loggers(object):
    	# 日志级别关系映射
    	level_relations = {
    		'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING,
    		'error': logging.ERROR, 'critical': logging.CRITICAL
    	}
    ​
    	def __init__(self, filename='{uid}.log'.format(uid=uuid.uuid4()), level='info', log_dir='log',
    				 fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
    		self.logger = logging.getLogger(filename)
    		abspath = os.path.dirname(os.path.abspath(__file__))
    		self.directory = os.path.join(abspath, log_dir)
    		format_str = logging.Formatter(fmt)  # 设置日志格式
    		self.logger.setLevel(self.level_relations.get(level))  # 设置日志级别
    		stream_handler = logging.StreamHandler()  # 往屏幕上输出
    		stream_handler.setFormatter(format_str)
    		file_handler = PathFileHandler(path=self.directory, filename=filename, mode='a')
    		file_handler.setFormatter(format_str)
    		self.logger.addHandler(stream_handler)
    		self.logger.addHandler(file_handler)
    ​
    ​
    if __name__ == "__main__":
    	txt = "谢谢关注"
    	log = Loggers(level='debug')
    	log.logger.info(4)
    	log.logger.info(5)
    	log.logger.info(txt)
    

    日志格式

    image

    日志的核心

    日志记录器Logger
    日志处理器Handler
    日志过滤器Filter
    日志的格式化 Formatter
    

    日志核心方法函数

    logging.getLogger(name) 默认没有name 返回root
    logging.baseConfig()配置root 记录器格式,处理器
    logging.info()/debug()/warning()/error()/由root记录器记录日志信息
    

    logger记录器核心方法

    setLevel(logging.DEBUG|INFO|WARNING|ERROR|CRITICAL)
    addHander(hander)
    debug()info()
    handler处理器核心方法
    

    Formatter初始化参数

    format 格式化字符串,使用`%(日志变量)s` 相关日志变量占位符组成的字符串
    'hi, %(name)s, age is %(age)s' % {'age': 20, 'name': 'jack'}
    'hi, %s, age is %s' % ('disen', 30)
    datefmt 指定 `%(asctime)s` 日志时间的格式, 通常使用 `%Y-%m-%d %H:%M:%S` 即`年月日 时分秒`格式。
    

    scrapy 框架

    核心
    engine 引擎 协调其他四个组件之间的联系,通信
    spider 爬虫类 爬虫发起请求经过engine 转入scheduler
    scheduler 调度器 调度所有的请求(优先级高先执行)当执行某一个请求时,engine 转入到downloader
    downloader 下载器,实现任务执行,从网络上请求数据,请求到数据封装成响应对象,响应对象返回给engine,engine将数据响应数据对象,回传给它的爬虫类对象解析
    itempipline 数据管道 ,当spider 解析完,将engine转入到此(数据管道),再根据数据类型,进数据处理
    爬虫中间件 介于spider 和engine 之间可以拦截spider 发起的请求和数据
    下载中间件 介于engine 和downloader 之间可以拦截下载和响应可以设置代理,请求头,cookie 可以基于splash和selenium实现特定操作
    

    scrapy指令

    创建项目指令:scrapy startproject 项目名
    创建爬虫命令 sccrapy genspider 爬虫名域名
    启动爬虫命令 scrapy crawl 爬虫名
    调试爬虫命令 scrapy shell url scrapy shell fetch
    

    Response

    属性相关
    	body 响应字节数据
    	text 响应编码后的文本数据
    	encoding 响应数据编码字符集
    	status 响应状态码
    	url 请求url
    	request 请求对象
    	meta 元数据 在request 和callback 回调函数之间传值
    解析
    	selector()
    	css()样式选择器 返回selector选择器可迭代对象
    	scrapy.selector.SelectorList选择器列表
    	x()/xpath()
    	scrapy.selector.Selector 选择器
    	样式选择器提取属性或文本
    	::text提取文本
    	::attr('属性名')提取属性
    	xpath()
    	选择器常用用法,css()xpath
    	extract() 提取所有内容返回是list
    	extract_first/get() 提取每个选择器中内容,返回文本
    	Request类
    	scrapy.http.Request
    		请求对象有 url callback解析数据回调函数对象 headers priority优先级
    	dont_filter 是否过滤重复的url,True不过滤,False过滤
    

    scrapy 数据管道

    scrapy crawl 爬虫名 -o xxx.json|csv
    

    item类

  • 相关阅读:
    知识图谱应急安全场景应用规划
    DataxWeb 设置运行错误
    Datax mysql 8.x elasticsearch 7.x 测试成功json样例
    testmysqltoelasticsearch76.json 未测试,仅参考
    testmysqltoelasticsearch75.json 未测试,仅参考
    testorcletoelasticsearch73.json 微测试,仅参考
    testmysqltoelasticsearch74.json 未测试,仅参考
    testmysqltoelasticsearch72.json 微测试,仅参考
    go可变参数
    Java 8 终于支持 Docker !
  • 原文地址:https://www.cnblogs.com/abldh12/p/15516233.html
Copyright © 2020-2023  润新知