两周从爬虫小白变大神,看完你就知道我不是标题党了【五万字教程,建议收藏】_yjs_js_security_passport(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

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())


#### 正则解析数据


#### 扩展Linux文件权限



100 ->4 -> r

010 -> 2-> w

001 -> 1 -> x



100 | 010 = 110 # 增加权限

110 & 100 == 100 # 验证100权限

110 ^ 100 = 010   # 删除100权限


#### re面试中的问题


* compile() /match()/search() 三者之间的区别
* search()/findall()区别
* 贪婪模式和非贪婪模式



#### 解析站长之家



“”"
基于正则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’(.*?)‘)
compile2 = re.compile(r’(.*?)‘)

imgs = compile.findall(html)  # 返回list
if len(imgs) == 0:
   imgs = compile2.findall(html)

print(len(imgs), imgs, sep=’\n’)

下一页

next_url = re.findall(r’20<a href=“(.*?)” class=“nextpage”',html, re.S)

print(base_url+next_url[0])



#### 作业


* 写出requests.request()方法常用的参数及参数类型


	+ method: str 请求方法, 可以指定 get, post, put, delete, options
	+ url : str 请求路径或api接口
	+ params/data/json : dict 上传的请求参数及json或form的data数据
	+ headers/cookie: dict 请求头或Cookie信息
	+ files: dict 上传的文件信息
* 写出正则的贪婪模式有哪些


	+ `.*` 0或多个任意字符
	+ `.+` 1或多个任意字符
	+ `.?`
	+ `.{n, }` 至少n个以上的任意字符
	+ `.{n, m}` 至少n个以上的任意字符
* 写出str对象的常用方法(10+)


	+ join()
	+ split()
	+ strip()
	+ replace()
	+ upper()
	+ lower()
	+ title()
	+ index()/rindex()
	+ find()/rfind()
	+ insert()
	+ just()/ljust()/rjust()
	+ capitalize() # 每个单词的首字母大写
	
	 
	```
	要求: 自动生成订单号
	订单号的格式: 20191226000001
	当天的单号按自增,第二天的序号是从1开始。
	```
	+ count()
	+ encode()
	+ startswith()/endswith()
	+ format()



1. 基于Flask实现文件上传服务器, 通过requests测试文件上传接口。
2. 优化美女网爬虫,将数据存到es搜索引擎中
3. 完成站长之家的多任务爬虫的数据存储(ES引擎/csv)


![](https://img-blog.csdnimg.cn/20210922220415743.gif)



### 爬虫第四天


#### 回顾知识点


#### re正则


* 字符的表示


	+ `.` 任意一个字符, 除了换行
	+ `[a-f]` 范围内的任意一个字符
	+ `\w` 字母、数字和下划线组成的任意的字符
	+ `\W`
	+ `\d`
	+ `\D`
	+ `\s`
	+ `\S`
* 量词(数量)表示


	+ `*` 0或多个
	+ `+` 1或多个
	+ `?` 0 或 1 个
	+ `{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) # 将text中的所有数字替换成120
	```
	 分享面试题:
	
	 
	```
	给定列表,每一个元组中包含字母和数字, 要求字母和数字分开排序
	如:
	['abc12', 'abc9', 'abc10', 'ac8', 'ac12']
	排序之后结果是:
	['abc9', 'abc10', 'abc12', 'ac8', 'ac12']
	```
	 
	```
	def format_number(item):
	    replace_number = re.findall(r'\d+',item)[0].rjust(2, '0')
	      return re.sub(r'\d+',replace_number,item)
	​
	arr = ['abc12', 'abc9', 'abc10', 'ac8', 'ac12']
	sorted(arr, key=format_number)
	```
	+ re.split()


#### 进程和线程


* multiprocessing模块(进程)


	+ Process 进程类
	+ Queue 进程间通信的队列
	
	
		- put(item, timeout)
		- item = get(timeout)
* threading 模块(线程)


	+ Thread 线程类
	+ 线程间通信(访问对象)
	
	
		- queue.Queue 线程队列
		- 回调函数(主线程声明, 子线程调用函数)


#### BS4数据解析


* 安装包

 pip install bs4
* from bs4 import BeautifulSoup
* 生成bs4根节点对象

 root = BeautifulSoup(html, 'lxml')
* 查找节点(bs4.element.Tag)


	+ root.find('标签名', class\_="", id\_="") 返回单节点Tag对象
	+ root.find\_all('标签名', class\_="", 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调度, 协程由用户(程序)自己的调度的。协程需要事件监听模型(事件循环器),它采用IO多路复用原理,在多个协程之间进行调度。
> 
> 
> 


#### 协程的三种方式


* 基于生成器 generator (过渡)


	+ yield
	+ send()
* Python3 之后引入了 asyncio模块


	+ @asyncio.coroutine 协程装饰器, 可以在函数上使用此装饰器,使得函数变成协程对象
	+ 在协程函数中,可以使用yield from 阻塞当前的协程,将执行的权限移交给 yield from 之后的协程对象。
	+ asyncio.get\_event\_loop() 获取事件循环模型对象, 等待所有的协程对象完成之后结束。
* Python3.5之后,引入两个关键字


	+ async 替代 @asyncio.coroutine
	+ await 替代 yield from


#### 协程第三方的框架


* gevent
* eventlet
* Tornado/Twisted


#### 动态js渲染


#### Selenium



> 
> Selenium是驱动浏览器(chrome, firefox, IE)进行浏览器相关操作(打开url, 点击网页中按钮功连接、输入文本)。
> 
> 
> 


在Python程序中使用时,需要selenium的库和相关浏览的驱动程序(Window, Linux, Mac)。



#### Splash



> 
> Splash 是Web服务, 基于WebKit技术框架,可以动态加载网页。
> 
> 
> 



#### 作业


* 写出生成dict对象的方式有哪些


	+ { }
	+ dict([(key, value), ..])
	+ json.loads('json格式的字符串')
	+ OrderDict
	+ dict(zip([ ], [ ]))
	+ dict.fromkeys([], value)
	+ dict(key=value, key=value)
* 写出bs4查找的节点对象的类是什么,它有哪些属性及方法


	+ bs4.element.Tag/bs4.Tag 节点对象的类
	+ Tag的方法
	
	
		- find()
		- find\_all()
		- select()
		- get\_text()
	+ Tag的属性
	
	
		- string 标签的文本
		- text 标签的文本
		- contents 所有文本的子节点
		- descendants 所有的子节点对象
		- attrs 属性字典
* 写出创建线程Thread类的实例时的参数有哪些

 提示: Thread(参数列表)


	+ name 线程名
	+ target 线程执行的目标函数
	+ args 函数中位置传参, tuple
	+ kwargs 指定函数中关键参数传值 , dict
* 使用docker搭建Splash服务
* 股票信息提取
* 腾讯公司招聘需求抓取


![](https://img-blog.csdnimg.cn/20210922220432383.gif)



### 爬虫最五天


#### 回顾知识点


#### 协程的爬虫


* 协程和线程区别

 

线程是CPU调度的,多线程是共享同一进程中的内存的(线程本地变量 Local、同步锁Lock、条件变量)。
线程是threading模块

 

协程是在线程(主)中声明及调度的。协程由用户(程序)调度的,是基于事件模型(IO多路复用模型-select/poll/epoll)。
协程是asyncio模块(Python 3.4+)

* 协程的知识点


	+ @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库


* 安装python库

 

pip install selenium

* 下载浏览器的驱动


	+ chrome
	+ firefox
* 在Python中使用


	+ selenium.webdriver.common.by.By
	
	
		- By.CLASS\_NAME
		- By.CSS\_SELECTOR
		- By.ID
		- By.NAME
		- By.TAG\_NAME
		- By.XPATH
		- By.LINK\_TEXT
	+ selenium.webdriver.Chrome
	
	
		- 在实例化Chrome()对象中, 需要指定driver.exe浏览驱动程序的位置。如果位置在环境变量的Path添加了,则不需要指定位置参数。
		- chrome.get(url) 打开url
		- chrome.find\_element(by, value) 根据by 查找value的一个元素。
		- chrome.find\_elements(by, value) 查找多个元素
		- chrome.window\_handlers: list 可以获取窗口标签页
		- chrome.execute\_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\_elements\_located((By, value))


#### Chrome-headless



options = Options()
  options.add_argument(‘–headless’)
  options.add_argument(‘–disable-gpu’)
  # options.binary_location=r’/Users/apple/PycharmProjects/xpy905_spider/day04/chromedriver’

  chrome = Chrome(options=options)


headless 无窗口


#### Splash渲染


#### 下载镜像


通过docker下载splash镜像


官方服务器



sudo docker pull scrapinghub/splash


私有的docker仓库下载



sudo docker pull 10.36.173.95:5000/splash


#### 启动镜像



sudo docker run -itd --name splash-s -p 8050:8050 10.36.173.95:5000/splash


-p 指定宿主机和容器的端口映射关系,


格式: `[宿主机端口]:[容器的端口]`


-d 后台启动, -t 可以打开容器的终端, -i 可进入容器


进入容器



sudo docker exec -it splash-s bash


未进入容器时,也可以通过`exec`命令执行容器中的命令



sudo docker exec splash-s ls -l


`ls -l` 命令会列出`splash-s`容器当前的目录下的所有文件。


#### render.html接口


渲染动态js的接口: [http://ip:port/render.html]( )


接口的参数:


* url 目标的网址
* wait 等待渲染或加载的时间
* proxy
* headers
* timeout


如,渲染jd.com网页



http://10.36.173.186:8050/render.html?url=https://jd.com


#### 自动化测试


#### 单元测试


Pythonu单元测试模块-unittest



from unittest import TestCase

class TestIndex(TestCase):
def setUp(self):
print(‘–测试前的资源准备工作—’)

def test_a_add_index(self):
    print('--添加索引--')
    data['index_name'] = 'person_sos'

def test_b_query_index(self):
    print('--查询索引--')

def test_c_delete_index(self):
    print('--删除索引--')

def tearDown(self):
    print('--测试后的资源回收工作---')


#### 集成测试


单元测试套件 unitest.TestSuit



“”"
使用单元测试,测试ES搜索引擎的RESTful接口

  • requests
  • unittest
    “”"

from unittest import TestCase, TestSuite, TextTestRunner

data = {

}

class TestDoc(TestCase):
def test_a2_add_doc(self):
print(f’-{data[“index_name”]}-增加doc文档–')

def test_a3_query_doc(self):
    print(f'-{data["index_name"]}-查询doc文档--')

if name == ‘main’:
# 必须以普通的Python脚本运行

suite = TestSuite()
suite.addTest(TestIndex.test_a_add_index)
suite.addTest(TestDoc.test_a2_add_doc)
suite.addTest(TestDoc.test_a3_query_doc)

TextTestRunner().run(suite)

#### 作业


* 写出Python协程的模块及核心函数
* 写出Selenium的查找元素的方式
* 写出Python获取命令行参数的方式


### 



### 爬虫第六天


#### 回顾知识点


#### 爬虫的认知



  • 数据请求(网络请求库)
  • 数据解析(re/xpath/bs4)
  • 数据存储(csv/pymysql/json??)
  • 反反爬的策略
    • ip代理
    • ua池
    • cookie池: 收集手动登录之后的响应的Cookie信息
    • 请求间隔(2~5秒)
    • 验证码处理(打码平台、机器学习???)

#### 网络请求库



  • urllib

    • request
      • urlopen()
      • urlretrieve(fullurl, filename)
      • Request(url, data=None, headers)
      • build_opener(*handlers)
      • HTTPHandler
      • HTTPCookieProcessor(http.cookiejar.CookieJar())
      • ProxyHandler(proxies={})
    • parse
      • quote()
      • urlencode()
  • http.client.HTTPResponse

    • code
    • getheaders()
    • getheader(name, default)
    • read() 读取的响应字节数据
  • requests (第三方)

    • request(method, url, params, data, json, files, headers, cookies, proxies, auth)
    • get(url, params, **kwargs)
    • post(url, data, json, **kwargs)
    • put(url, data, json, **kwargs)
    • delete(url, **kwargs)
    • Response
      • status_code
      • encoding
      • headers
      • content 字节数据
      • text 文本数据
      • json() json文本反序列化为Python的dict/list的对象

#### 数据解析



  • re

  • xpath (pip install lxml)

    • from lxml import etree
      root = etree.HTML(html)
      root.xpath(‘’) # list[‘’] / list[, ]
    • 返回文本列表的xpath表示
      • @href/@src 标签属性
      • text() 标签文本
    • 返回Element元素列表
      • //title
      • //ul/li[1]
  • bs4 (pip install bs4)

    • from bs4 import BeautifulSoup
      root = BeautifulSoup(html, ‘lxml’) # bs4.element.Tag

    • 查询元素标签的方法

      • find(‘标签名’, class_, id_) 查找第一个
      • find_all(‘标签名’, class_, id_, limit=N) 查找前N个
      • select(‘css选择器’)
        • #id
        • .classname
        • 标签名
        • 后代标签
        • 兄弟标签 (查找多个标签)
        • 属性标签
        • 伪类
    • Tag属性

      • string/text
      • get_text()
      • attrs: dict 标签中所有属性的字典
      • contents 子标签的文本列表
      • descendants 子标签的Tag列表

#### 多任务爬虫


* 多线程


	+ threading
	
	
		- Thread
	+ queue.Queue 线程队列
* 多进程


	+ multiprocessing
	
	
		- Process
		- Queue 进程队列
* 协程


	+ asyncio
	
	
		- coroutine 协程装饰器
		- get\_event\_loop()
		- wait()
		- sleep()
	+ yield from
	+ async / await


#### selenium框架



以driver程序驱动浏览器,对目标(网站或网页)进行操作(请求网页、提取数据、截图、切换或关闭页签-window)。



  • chrome.get() 打开目标(发起请求)

  • chrome.quit() 退出浏览器

  • chrome.close() 关闭当前的窗口

  • chrome.find_element(By, value)

    • selenium.webdriver.common.by.By
      • ID
      • CLASS_NAME
      • NAME
      • XPATH
      • CSS_SELECTOR
        _ LINK_TEXT
    • WebElement 查到的标签对象
      • get_attribute(‘属性名’, default)
      • text 标签文本
      • click()
      • send_keys()
      • rect 当前元素的位置(left, top, width, height)
  • chrome.find_elements(By, value)

  • execute_script()

  • save_screenshot(filename) 截图

  • 等待某一个标签元素出现

    • selenium.webdriver.support
      • ui
        • WebDriverWait
      • expected_conditions
        • visibility_of_all_elements_located((By, value))

    ui.WebDriverWait(dirver, timeout).until(
    expected_conditions,
    error_msg
    )


#### 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强制删除正运行的容器


#### 日志模块进阶


#### 日志格式




| 格式 | 说明 |
| --- | --- |
| %(name)s | 记录器的名称, 默认为root |
| %(levelno)s | 数字形式的日志记录级别 |
| %(levelname)s | 日志记录级别的文本名称 |
| %(filename)s | 执行日志记录调用的源文件的文件名称 |
| %(pathname)s | 执行日志记录调用的源文件的路径名称 |
| %(funcName)s | 执行日志记录调用的函数名称 |
| %(module)s | 执行日志记录调用的模块名称 |
| %(lineno)s | 执行日志记录调用的行号 |
| %(created)s | 执行日志记录的时间 |
| %(asctime)s | 日期和时间 |
| %(msecs)s | 毫秒部分 |
| %(thread)d | 线程ID |
| %(threadName)s | 线程名称 |
| %(process)d | 进程ID |
| %(message)s | 记录的消息 |



  • Python脚本中执行当前操作系统的命令的方法
    • os.chdir() 切换当前目录
      • os.system() 无返回结果 (打开一个子进程执行 命令)
      • os.popen() 可读取返回结果

#### 日志模块的核心


* 四大核心


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


#### scrapy框架


#### scrapy架构组成


* 五个核心


	+ **engine 引擎**, 协调其它四个组件之间的联系,即与其它四个组件进行通信,也是scrapy框架的核心。
	+ **spider 爬虫类**, 爬虫程序的编写代码所在, 也是发起请求的开始的位置。spider发起的请求,经过engine转入到scheduler中。
	+ **scheduler 调度器**, 调度所有的请求(优先级高,则会先执行)。当执行某一个请求时,由engine转入到downloader中。
	+ **donwloader 下载器**, 实现请求任务的执行,从网络上请求数据,将请求到的数据封装成响应对象,并将响应的对象返回给engine。engine将数据响应的数据对象(以回调接口方式)回传给它的爬虫类对象进行解析。
	+ **itempipeline 数据管道**, 当spider解析完成后,将数据经engine转入到此(数据管道)。再根据数据类型,进行数据处理(图片、文本)
* 二个中间件


	+ **爬虫中间件**, 介于Spider和Engine之间的,可以拦截Spider的发起的请求及数据。
	+ **下载中间件**,介于Engine和Downloader之间的,可以拦截下载和响应。当然在下载处理之前,可以设置代理 、请求头、Cookie等操作(反反爬设置),还可以基于Splash或Selenium实现特定的操作。


#### scrapy指令


* 创建项目命令


	+ scrapy startproject 项目名称
* 创建爬虫命令


	+ scrapy genspider 爬虫名 域名
* 启动爬虫命令


	+ scrapy crawl 爬虫名
* 调试爬虫命令


	+ scrapy shell url
	+ scrapy shell
	
	
		- fetch(url)


#### Response类


* 属性相关【重点】


	+ body 响应的字节数据
	+ text 响应的编码之后文本数据
	+ headers 响应头信息, 是字节数据
	+ encoding 响应数据的编码字符集
	+ status 响应的状态码
	+ url 请求的url
	+ request 请求对象
	+ meta 元数据,用于request和callback回调函数之间传值
* 解析相关【重点】


	+ selector()
	+ css() 样式选择器 , 返回Selector选择器的可迭代(列表)对象
	
	
		- scrapy.selector.SelectorList 选择器列表
		
		
			* x()/xpath()
		- scrapy.selector.Selector 选择器
		- 样式选择器提取属性或文本
		
		
			* `::text` 提取文本
			* `::attr("属性名")` 提取属性
	+ xpath() xpath路径
	
	 xpath路径,同lxml的xpath()写法
	+ 选择器常用方法
	
	
		- css()/xpath()
		- extract() 提取选择中所有内容,返回是list
		- extract\_first()/get() 提取每个选择器中的内容, 返回是文本


#### Request类


* scrapy.http.Request

 请求对象的属性


	+ url
	+ callback 解释数据的回调函数对象
	+ headers 请求头
	+ priority 请求的优先级, 值越高,优先级越高(优先下载)


#### 作业


* 写出selenium向下和向右滚动的脚本

 document.documentElement.scrollTop 向下

 document.documentElement.scrollLeft 向右
* 写出restful接口设计规范(四个)


	+ 每个资源都有唯一标识 URI
	+ 每个资源具有四个动作, GET|POST|PUT|DELETE
	+ 每次请求都是无状态
	+ 接口交互的数据是json或xml
* 写出常见的反爬虫和反反爬虫


	+ 访问次数 - IP代理
	+ Cookie验证- Cookie池
	+ UA验证 - UA池
	+ 验证码 - 打码平台
	+ 动态js渲染 - Selenium/Splash
* 爬取 [陕西省政府采购网]( ) 陕西省采购网
* 基于Flask实现日志上报服务器(日志微服务)


	+ logging.handlers.HTTPHandler


![](https://img-blog.csdnimg.cn/20210922220513606.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5LqU5YyF6L6j5p2h77yB,size_20,color_FFFFFF,t_70,g_se,x_16)



### 爬虫第七天


#### 回顾知识点


#### 日志模块



import logging
from logging import StreamHandler, FileHandler


* 四个核心部分


	+ 日志记录器logger: 记录日志信息
	+ 日志处理器 handler: 记录信息之后,由handler去处理
	+ 日志过滤器 filter: 对记录信息进行过滤。
	+ 日志格式化 formatter: 由处理器对记录的信息按formatter格式进行处理(除HTTPHandler和SMTPHandler之外)。
* 核心方法或函数


	+ logging.getLogger(name) # 默认没有name时,返回root
	+ logging.baseConfig() 配置root记录器的格式、处理器等。
	+ logging.info()/debug()/warning()/error()/critical() 由root记录器记录日志信息。
* logger记录器的核心方法


	+ setLevel(logging.DEBUG|INFO|WARNING|ERROR|FATAL)
	+ addHandler(handler)
	+ addFilter(Filter)
	+ debug()|info()….
* handler处理器的核心方法


	+ setLevel(logging.DEBUG|INFO|WARNING|ERROR|FATAL)
	+ setFormatter(fmt)
* 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 爬虫类
* scheduler 调度器
* downloader 下载器
* itempipeline 数据管道
* 爬虫中间件、下载中间件


scrapy指令


* scrapy startproject 项目名
* scrapy genspider 爬虫名 域名
* scrapy crawl 爬虫名


	+ -o 保存数据到指定的文件中
	+ -s 信号(CLOSESPIDER\_ITEMCOUNT=30)
* scrapy shell [url]


	+ fetch(url)
	+ view(response)
	+ request: scrapy.http.Request
	+ response: scrapy.http.Response|HtmlResponse
	+ scrapy


Response对象的属性或方法


* body|text|encoding|status|url|request|headers|meta
* xpath()|css() -> scrapy.selector.SelectorList[Selector]


	+ extract()
	+ get()
	+ extract\_first()
* css() 中表达式


	+ `样式选择器[::text|attr("属性名")]`
* xpath()中表达式

 同lxml的xpath表达式相同。


Request初始化参


* url
* callback 如果未指定,则默认为 `parse`
* priority 优先级的权限值, 值高优先级高
* meta
* headers
* dont\_filter 是否过滤重复的url, True不过滤,Flase过滤.


#### scrapy数据管道


#### 指令方式存储



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


只适合单页数据爬取,如果多页多层次数据爬取时,不适合此方式。


Item类


作用: 用于区别中哪一页(类型)的数据


用法: 类似于dict用法, 在数据管道类的process\_item()方法中,通过isinstance()方法来判断item是哪一类型的。



import scrapy

class BookItem(scrapy.Item):
   book_id = scrapy.Field()
   book_name = scrapy.Field()
   book_cover = scrapy.Field()
   book_url = scrapy.Field()
   author = scrapy.Field()
   tags = scrapy.Field()
   summary = scrapy.Field()


class SegItem(scrapy.Item):
   book_id = scrapy.Field()
   seg_id = scrapy.Field()  # 章节ID
   title = scrapy.Field()
   url = scrapy.Field()


class SegDetailItem(scrapy.Item):
   seg_id = scrapy.Field()  # 章节ID
   content = scrapy.Field()  # 内容


#### Pipeline


* 处理数据的方法

 

def process_item(self, item, spider):
return item


	+ item参数表示 爬虫类中解释到的数据(yield item)
	+ spider参数 表示爬虫类对象
	+ 如果item被返回,则表示可以被优先级低的pipeline处理
* 初始化方法

 属于定制方法,可以初始化一些参数或对象,如文件名, 数据库的连接等。
* `process_item`和`init`的调用次数说明


	+ `process_item`方法 会被(engine)多次调用
	+ `init`随着爬虫程序的启动时创建pipeline类时调用,只会被调用一次


#### 定量爬虫


#### 基于信号方式



scrapy crawl -s 信号


常用的scrapy信号


* **CLOSESPIDER\_ITEMCOUNT=**条目的数量
* CLOSESPIDER\_PAGECOUNT=请求页的数量
* CLOSESPIDER\_ERRORCOUNT=请求错误的数量
* CLOSESPIDER\_TIMEOUT=超时的时长



scrapy crawl wanben -s CLOSESPIDER_ITEMCOUNT=10



#### 下载中间件


#### 爬虫中间件



监测爬虫类与引擎之间的交互数据(请求 request、响应 response、数据item)及异常情况



@classmethod
def from_crawler(cls, crawler): pass  # 启动爬虫时用于创建爬虫中间件类的实例对象

def process_spider_input(self, response, spider) # 流程中第6步,engine将请求响应的数据输入给spider时,调用此方法。

def process_spider_output(self, response, result, spider) # 流程中第7步,由spider类解析response数据之后产生结果输出给engine时,调用此方法

def process_spider_exception(self, response, exception, spider): # 解析数据时发异常时
 
def process_start_requests(self, start_requests, spider): # 第一次爬虫发起请求时,调用此方法,即流程中第1步,从Spider->Engine时。


#### 下载中间件 [重点]



下载中间件是引擎engine和下载器downloader之间的中间件,可以拦截请求和响应以及请求异常的处理。



@classmethod
def from_crawler(cls, crawler)

def process_request(self, request, spider)

def process_response(self, request, response, spider)

def process_exception(self, request, exception, spider)


* **process\_request()**方法可返回的对象(四种可能)


	+ scrapy.http.Request
	+ scrapy.http.HtmlResponse/Response
	+ None 表示不拦截
	+ raise IgnoreRequest 不下载这个请求
* **process\_response()**方法可以返回的对象


	+ scrapy.http.Request 未下载成功请求
	+ scrapy.http.Response 进一步封装之后的response


#### 4.3 作用


在下载中间件中,可以设置代理、设置cookie、设置请求头以及基于Selenium实现动态js渲染和用户登录。



#### 作业


* 写出scrapy的工作流程中第3、6两个步骤的描述

 

第3步: 引擎从调度器中获取下载任务, scheduler -> engine
第6步: 引擎从下载器获取的响应传递给spider, 用于解析。engine-> spider。 ( response.request.callback(response) )

* 写出logging.baseConfig()方法的参数(4+)

 

logging.baseConfig(filename,  # 文件处理器参数
                  mode=“a”,  # 文件处理器参数
                  format,    # formatter
                  datefmt,   # formatter
                  handlers,  # addHandler(),
                  filters,   # addFilter()
                  stream)    # filename设置后, stream无效

* 根据左边AA表结构和右边的查询结果,写出查询的SQL

 

---------------------------       ----------------------
| year | month | amount |       | year | m1 |   m2 |
---------------------------       ----------------------
| 1991 |   1   | 1.1   |       | 1991 | 1.1 | 1.2 |
---------------------------       ----------------------
| 1991 |   2   | 1.2   |       | 1992 | 2.1 | 2.2 |
---------------------------       ----------------------
| 1992 |   1   | 2.1   |

| 1992 |   2   | 2.2   |


	+ join连接表方式
	
	 
	```
	select a1.year, a1.amount as m1, a2.amount as m2
	from AA a1
	join AA a2 on (a1.year = a2.year)
	where a1.month=1
	and a2.month=2;
	```
	+ if/case判断函数方式
	
	 
	```
	select if(条件, 成立的结果, 不成立的结果);
	```
	 
	```
	select year, 
				max(round(if(month=1, amount, 0),1)) m1, 
				max(round(if(month=2, amount, 0),1)) m2
	from AA
	group by year;
	```
	 
	```
	select year, 
	       max(round(m1, 1)) as m1,
	       max(round(m2, 1)) as m2
	from (
	    select year,
	           case when month=1 then amount else 0 end as m1,
	           case when month=2 then amount else 0 end as m2
	    from AA
	) a
	group by year;
	```
	 
	```
	select year, 
	       max(round(m1, 1)) as m1,
	       max(round(m2, 1)) as m2
	from (
	    select year,
	           case month when 1 then amount else 0 end as m1,
	           case month when 2 then amount else 0 end as m2
	    from AA
	) a
	group by year;
	```


![](https://img-blog.csdnimg.cn/20210922220534771.gif)



### 爬虫第八天


#### 回顾知识点


#### 数据处理


* 启动爬虫的指令中带有 -o参数, 指定数据存储的文件(csv/json/xml)位置
* 数据管道 pipeline


	+ settings.py中配置管道类
	+ process\_item(self, item, spider) 管道类的处理数据的方法
	
	
		- 通过**isinstance()** 判断item属于哪一种类型, 然后按某一类型进行处理
		- 扩展 Python的自省相关函数
		
		 
		```
		type(obj)  获取对象的类型
		dir(obj)   获取对象中所有的属性(函数、类、变量)
		help(obj)  获取对象的文档帮助信息
		isinstance(obj, 类) 判断obj是否为类的实例对象
		issubclass(类, 父类) 判断类是否为父类的子类
		hasattr(对象, 属性或方法)  判断对象中是否存在属性或方法
		getattr(对象, 属性或方法)  获取对象中的属性或方法
		setattr(对象, 属性, 属性值) 设置对象的属性或方法
		id(对象) 获取对象在内存中的唯一标识
		```
		- 返回item: 目的是让优先级低的数据管道类可以接收到item数据。


#### 中间件


* 爬虫中间件(scrapy工作流中第1, 第6, 第7等3个步)

 

@classmethod
def from_crawler(self, crawler)

def process_spider_input(self, response, spider)

def process_spider_output(self, response, result, spider)

def process_spider_exception(self, response, exception, spider)

def process_start_requetst(self, start_requests, spider)

def spider_opened(self, spider)

* 下载中间件 (第4, 5两个步骤)

 

@classmethod
def from_crawler(self, crawler)

def process_request(self, request, spider):
 return None|Request|Response| raise IgnoreRequest

def process_response(self, request, response, spider)
return response|request
 
def process_exception(self, request, excecption, spider)

def spider_opened(self, spider)




#### 规则爬虫


* 创建规则爬虫的指令

 

scrapy genspider -t crawl 爬虫名 域名

* 链接提取器 LinkExctractor


	+ 正则方式 (allow | deny)
	+ restrict\_xpaths() xpath方式指定`a`标签所在的(间接)父级标签
	+ restrict\_css() 样式方式指定`a`标签所在的(间接)父级标签
* Rule() 规则


	+ extractor: LinkExtractor
	+ callback: str
	+ follow=True 表示提取的连接在请求成功后,解析时是否继续按此规则提取连接
* 不能重写parse()函数 【注】


#### 规则爬虫【重】


#### LinkExtractor 类


作用: 提取感兴趣的`a`标签中的连接`href`属性, 因此在指定正则表过式,参考某些`a`标签中`href`属性的写法。如果正则提取困难,则支持css或xpath两个方式来指定`a`标签所在的父级标签。


#### 核心的类


* scrapy.spiders.CrawlSpider 规则爬虫类

 重写了parse()解析函数,在此函数中通过指定规则中的LinkExtractor对象来提取当前响应数据中的连接,并向engine发起新的请求。新的请求中包含提取的连接url和rule中的回调函数。

 

def parse(self, response):
       return self._parse_response(response,
      self.parse_start_url, cb_kwargs={}, follow=True)

def _parse_response(self, response, callback, cb_kwargs, follow=True):
   if callback:
       cb_res = callback(response, **cb_kwargs) or ()
       cb_res = self.process_results(response, cb_res)
       for requests_or_item in iterate_spider_output(cb_res):
           yield requests_or_item

   if follow and self._follow_links:
       for request_or_item in self._requests_to_follow(response):
           yield request_or_item
           
def _requests_to_follow(self, response):
   if not isinstance(response, HtmlResponse):
       return
   seen = set()
   for n, rule in enumerate(self._rules):
       links = [lnk for lnk in rule.link_extractor.extract_links(response)
                if lnk not in seen]
       if links and rule.process_links:
           links = rule.process_links(links)
       for link in links:
           seen.add(link)
           r = self._build_request(n, link)
           yield rule.process_request®

* scrapy.spiders.Rule 规则类


	+ extractor: LinkExtractor
	+ callback:str
	+ follow:bool
* scrapy.linkextractors.LinkExtractor 链接提取器类


	+ allow
	+ deny
	+ restrict\_xpaths
	+ restrict\_css
* 创建规则爬虫是使用 -t crawl 模板

 

scrapy genspider -t crawl 爬虫名 域名



#### 图片管道


#### 使用ImagesPipeline


* settings.py配置


	+ IMAGES\_STORE 指定数据存放的位置
	+ 在`ITEM_PIPELINES`字典中,引入`scrapy.pipelines.images.ImagesPipeline`
	+ 配置缩略图
	
	 
	```
	IMAGES_THUMBS = {
	     'small': (with, height),
	     'big': (widht, height)
	}
	```
* item数据属性


	+ image\_urls: list 表示下载图片地址的列表
	+ images: list 表示下载完成后的图片存放在`IMAGES_STORE`中的路径及相关的属性



#### 自定义ImagesPipeline



> 
> 实现ImagesPipeline的子类,重写三个核心的方法。
> 
> 
> 


三个核心的方法:


* get\_media\_requests(self, item, info) 根据item中图片连接的属性,返回图片下载的request。

 可以返回一个Request也可以多个Request的列表
* file\_path(self, request, response, info) 根据请求和响应返回图片保存的位置(相对于IMAGES\_STORE)。

 如果返回的路径中包含子路径时,系统会自动创建子目录( os.makedirs() )。
* item\_completed(self, results, item, info) 图片下载完成后,从results的结果获取图片的保存路径,并设置到item中,最后返回这个item。

 results格式

 

[
  (True, {‘path’: ‘’, ‘url’: ‘’, chucksum: ’ '} ),
  (True, {‘path’: ‘’, ‘url’: ‘’, })
]

 ####


#### 其它技术点


#### 日志



> 
> scrapy的日志记录器在爬虫类对象中, 是logger, 通过爬虫对象的logger来记录运行的信息。scrapy的logger日志记录器使用了Adpater设计模式的logging.LoggerAdapter类。
> 
> 
> 


【扩展】构建器设计模式 Builder ( 函数式-流式编程 )



car: Car = CarBuilder().step1().step2().step3().step4().build()



text = response.xpath().css().xpath().css().xpath().get()


Python开发人员需要掌握的设计模式: 单例、工厂、装饰器、适配器、构建器、生产者消费者(消息队列-订阅/发布)。


配置


在settings.py文件中,指定收集日志的等级及日志存储的文件名



LOG_LEVEL = ‘’ # DEBUG|INFO|ERROR|WARNING|CRITICAL
LOG_FILE = ‘文件名’ # os.path.join(BASE_DIR, ‘access.log’)


在程序中使用


* 爬虫日志记录器



spider.logger.info()/error()


* 获取scrapy中其它的记录器


	+ 内置的记录器
  • scrapy.utils.log
  • scrapy.crawler
  • scrapy.middleware
  • scrapy.downloadermiddlewares.httpauth|downloadtimeout…
  • scrapy.core.engine
  • scrapy.utils.signal
 

logging.getLogger(‘scrapy.utils.log’).info()/warning()

* 自定义记录器

 

error_logger = logging.getLogger(‘dushu-project’)
error_logger.setLevel(logging.ERROR)

handler = logging.FileHandler(
os.path.join(BASE_DIR, ‘error.log’),
encoding=‘utf-8’
)
handler.setLevel(logging.ERROR)
handler.setFormatter(logging.Formatter(
‘%(asctime)s %(name)s at %(lineno)s of %(pathname)s : %(message)s’
))
error_logger.addHandler(handler)

 使用时

 

from dushu.settings import error_logger

error_logger.error(‘消息’)



#### post请求


* scrapy.http.FormRequest


	+ url
	+ formdata: dict( value必须都是字符串类型)
	+ cookies: dict
	+ headers: dict
	+ callback 默认parse
* 如果爬虫中第一次的请求是post请求,则重写Spider类的start\_requests()方法

 

def start_requests(self):
self.url = ‘http://ccgp-shaanxi.gov.cn/notice/noticeaframe.do?noticetype=3&province=province&isgovertment=’
self.data = {
‘page.pageNum’: ‘1’
}
self.MAX_PAGE = 1399
yield FormRequest(self.url, formdata=self.data)

 不需要指定start\_urls 列表。


#### Selenium中间件


作用: 动态渲染js(ajax加载数据)


4.3.1 定义下载中间件类



class LoadDataMiddleware():
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)

    return s

* 连接打开爬虫类的信号,在处理函数中打开chrome



def spider_opened(self, spider):
    # 创建Selenium的Chrome浏览器对象
    # chromedriver.exec 所在的目录已配置到环境变量Path中
    options = Options()
    options.add_argument('--headless')
    options.add_argument('--disable-gpu')

    self.chrome = Chrome(options=options)

* 连接关闭爬虫类的信号,在处理函数中退出chrome
* ```
def spider_closed(self, spider):
       self.chrome.quit()
  • 处理process_request()方法编写核心业务
 def process_request(self, request, spider):
        # 判断是否为第一次请求
        if not request.meta.get('next_page', False):
            self.chrome.get(request.url)
        else:
            # 点击下一页
            self.chrome.execute_script('var q=document.documentElement.scrollTop=1200')
            time.sleep(0.5)
            self.chrome.execute_script('var q=document.documentElement.scrollLeft=1000')
            time.sleep(0.5)
            self.chrome.find_elements_by_xpath('//ul[@class="pagination"]/li/a')[-2].click()

        time.sleep(2)
        html = self.chrome.page_source
        return HtmlResponse(request.url, body=html.encode('utf-8'))

配置

DOWNLOADER_MIDDLEWARES = {
   'caigou.middlewares.LoadDataMiddleware': 543,
}

爬虫类

class ShaanxiSpider(scrapy.Spider):
    name = 'shaanxi2'
    allowed_domains = ['ccgp-shaanxi.gov.cn']
    # start_urls = ['http://ccgp-shaanxi.gov.cn/notice/noticeaframe.do?noticetype=3&province=province&isgovertment=']
    start_urls = ['http://ccgp-shaanxi.gov.cn/notice/list.do?noticetype=3&province=province']

    def parse(self, response):
        trs = response.css('.list-box tbody tr')
        for tr in trs:
            item = {}
            item['id'] = tr.xpath('./td[1]/text()').get()
            item['area'] = tr.xpath('./td[2]/text()').get()
            item['title'] = tr.xpath('./td[3]/a/text()').get()
            item['url'] = tr.xpath('./td[3]/a/@href').get()
            item['date'] = tr.xpath('./td[4]/text()').get()

            yield item

        # 获取下一页数据
        if len(trs) == 15:
            yield Request(response.request.url,
                          meta={'next_page': True}, dont_filter=True)

dont_filter=True 原因是可能会被认为是重复, 让engine不去过滤重复的url。

作业
  • 定量爬虫的指令有哪些
CLOSESPIDER_ITEMCOUNT  item数据条目量
CLOSESPIDER_PAGECOUNT  请求成功响应的次数
  • 下载中间件类的处理请求的方法是什么,可以返回哪些对象
def process_request(self, request, spider):
    return None|Request|Response | rasie IgnoreRequest
+ None 表示不拦截当前的请求
+ Request 重新返回新的请求, engine将这个新请求压入到Scheduler中
+ Response 表示不需要下载器下载,由自己程序下载并且封装成Response对象。
+ raise IgnoreRequest 取消当前的request请求(重复, 过滤的)
  • 简述中间件from_crawler(self, crawler)函数的作用
当爬虫程序启动后,用于创建当前中间件类实例的方法。
可以监听爬虫程序是否正常启动。

【扩展】类方法、静态方法和实例方法的区别??

类方法:类对象(由元类创建类的对象)的方法,第一个参数是cls(表示当前类本身)。
	     @classmethod 修饰的方法
类实例方法: 由类的__new__()函数处理的类实例对象的方法,第一个参数是self(表示当前类的实例)
静态方法: 和类没有任何关系,只是在当前类中声明,方法的参数不会出现cls和self。
         @staticmethod 修改的方法

【扩展】什么是抽象方法?

抽象方法是在父类中声明(没有实现功能),由子类实现的方法。如果子类中未实现,则会在调用时报错。
class Animal():
  def eat(self):  # 抽象方法
     raise Exception('子类中必须实现此方法!')
class Pig(Animal):
   def eat(self):
      print('Pig eat ', 'ddd')
      
class Dog(Animal):
   def eat(self):
      print('Dog eat ', 'abc')

爬虫第九天

回顾知识点
规则爬虫
  • scrapy.spiders.CrawlSpider 所有规则爬虫类的父类

  • scrapy.spiders.Rule 规则类

    • LinkExtractor 连接提取类的实例对象
    • callback: str 指定提取器提取的连接请求成功之后的数据解析的函数
    • follow: bool 表示对提取连接请求成功后的数据是否继续提取连接
  • scrapy.linkextractors.LinkExtractor

    • allow: str 提取连接中的href的正则表示
    • deny: str 拒绝提取连接中的href的正则表示
    • restrict_xpaths 以xpath的路径方式指定a所在的父级标签
    • restrict_css 以css的样式选择器方式指定a所在的父级标签
  • 创建规则爬虫的指令

scrapy genspider -t crawl 爬虫名 域名
日志
  • logging.LoggerAdapter 日志适配器类, 封装日志记录器和spider等相关消息
  • 每个爬虫类都存在它的日志记录器,记录器名称即是爬虫名(self.name)
  • 在中间件或管道等相关处理数据(item/request/response)的方法中都存在spider对象
spider.logger.info()/error()..记录日志
  • 在settings.py中配置日志记录的等级及日志文件存储的位置
LOG_LEVEL='INFO'
LOG_FILE = '/var/log/xxx.log'
  • 可以自定义日志记录器(可以声明一个日志处理模块-log_
dushu
  |---dushu
          |---spiders
          |---item.py
          |---middleware.py
          |---pipeline.py
          |---settings.py
          |---log_
                |--__init__.py
  |---scrapy.cfg

log_.__init__.py文件中的内容:

import logging
from logging import Formatter, FileHandler
​
logger = logging.getLogger('dushu')
logger.setLevel(logging.INFO)
​
handler = FileHandler('/var/log/dushu.log')
handler.setLevel(logging.INFO)
handler.setFormatter(Formatter(format='%(asctime)s:  %(message)s', 
                               datefmt='%Y-%m-%d %H:%M:%S'))
​
logger.addHandler(handler)
from dushu.log_ import logger
​
logger.info('记录的信息')
ImagesPipeline
  • scrapy.pipelines.images.ImagesPipeline 配置在settings.py的ITEM_PIPELINES={}
  • 在settings.py文件配置图片存储的位置
IMAGES_STORE = '/var/src/images'
  • 在爬虫类的parse()中,指定item的image_urlsimages

  • 自定义ImagesPipeline

    • 声明ImagesPipeline的子类
    • 重写get_media_requests(self, item, info)方法, 在此方法中获取item的图片下载地址,返回相关的Request(url, meta={‘name’: item[‘name’]})
    • 重写file_path(self, request, response, info)方法, 在此方法中返回相对于IMAGEs_STORE图片存储的相对路径
    return '%s/%s.jpg' %(dir_name, file_name)
    
    • 重写item_completed(self, results, item, info),在此方法中,从results中获取下载文件存储的路径, 并添加到item中
    path = [ data['path'] for ok, data in results if ok]
    item['path'] = ','.join(path)
    
    return item
    
Selenium下载中间件
  • 声明一个类, 并将此类配置在DOWNLOADER_MIDDLEWARES={}

  • 声明from_crawler(self, crawler) 创建当前中间件类的实例的, 在此方法中指定spider_openedspider_closed两个信号的处理方法

    • 在spider_opened()方法中,创建Chrome浏览器的驱动对象
    • 在spider_closed()方法, 将chrome浏览器对象进行退出
  • 声明process_request(self, request, spider) 处理每个请求

self.chrome.get(request.url)
# ....等待某个UI元素出现

html = self.chrome.page_sources

return scrapy.http.HtmlResponse(request.url, body=html.encode('utf-8'))
分布式爬虫
什么是分布式
  • Hadoop 分布式计算框架(大数据处理)HDFS(分布式文件系统)

    • MapReduce
    • Hbase 数据库
    • Hive 实时数据库
    • Spark 大数据平台(MySQL、Hbase)
  • 由多个服务器(操作系统-PC)组成,在调度器调度的情况下完成不同的任务,这种架构称之为分布式。常见的调度器是消息中间件、服务注册中心、负载均衡等组成。

常见消息队列
  • Redis订阅与发布-实现消息队列
  • RabbitMQ 基于Channel实现消息队列
  • Kafka 消息队列
scrapy-redis
  • 安装包: pip install scrapy-redis
  • 配置调度器类、去重类及消息中间件(队列的位置)
SCHEDULER="scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST=True
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

REDIS_URL='reids://[:password@]host-ip:6379/1' # 0-15数据库索引
  • 修改爬虫类

    • 父类 scrapy_redis.spiders.RedisSpider|RedisCrawlSpider
    • 去掉start_urls 列表
    • 增加redis_key 字符串变量, 指定redis服务中存储的key
  • 按正常启动爬虫程序命令启动爬虫

scrapy crawl 爬虫名
  • 连接redis服务,向redis_key的列表list中推送请求任务
lpush xxxx http://www.xxxx.com/xxx/
爬虫程序部署
scrapyd
  • 安装scrapyd服务和客户端
pip install scrapyd scrapyd-client
  • 如果在云服务器安装scrapyd

    • 修改scrapyd源码中app.py文件
    bind_addres = '0.0.0.0'
    
    • 在云服务的安全组中,放开6800端口
  • 在scrapyd服务启动Python环境中安装爬虫需要的依赖库

  • 修改爬虫项目scrapy.cfg文件

[deploy:100]
url = http://119.3.170.97:6800/
project = dushu_redis
  • 使用scrapyd-deploy命令发布项目
scrapyd-deploy 100 -p dushu_redis
docker部署

编写Dockerfile文件

FROM 119.3.170.97:5000/ubuntu:latest
MAINTAINER disen 610039018@qq.com
ADD . /usr/src
WORKDIR /usr/src
VOLUME /usr/src
RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
RUN chmod +x run.sh
CMD /usr/src/run.sh
  • ADD 命令将当前目录下所有文件复制到容器的/usr/src目录下
  • WORKDIR 容器中切换当前的工作目录,类似于cd
  • VOLUME 将容器中的文件位置暴露给外部宿主机, 在启动镜像时,通过 docker run -v同步宿主和容器之间的文件目录
  • RUN 执行容器中的命令, 可以执行多次
  • CMD 是当容器启动时执行的命令,且Docker中只能使用一次

编写shell文件

#!/bin/bash
cd /usr/src
scrapy crawl guoxue

构建镜像

在执行docker build之前,需要将sh文件改为可执行, 且文件格式改为UNIX。

vi run.sh文件,按shift+:进入命令行,执行以下指令:

:set ff=unix
docker build -t dushu:1.0 .

成功之后,查看镜像是否存在

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

网络安全面试题

绿盟护网行动

还有大家最喜欢的黑客技术

网络安全源码合集+工具包

所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

.0.0.0’
```
+ 在云服务的安全组中,放开6800端口

  • 在scrapyd服务启动Python环境中安装爬虫需要的依赖库
  • 修改爬虫项目scrapy.cfg文件
[deploy:100]
url = http://119.3.170.97:6800/
project = dushu_redis
  • 使用scrapyd-deploy命令发布项目
scrapyd-deploy 100 -p dushu_redis
docker部署

编写Dockerfile文件

FROM 119.3.170.97:5000/ubuntu:latest
MAINTAINER disen 610039018@qq.com
ADD . /usr/src
WORKDIR /usr/src
VOLUME /usr/src
RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
RUN chmod +x run.sh
CMD /usr/src/run.sh
  • ADD 命令将当前目录下所有文件复制到容器的/usr/src目录下
  • WORKDIR 容器中切换当前的工作目录,类似于cd
  • VOLUME 将容器中的文件位置暴露给外部宿主机, 在启动镜像时,通过 docker run -v同步宿主和容器之间的文件目录
  • RUN 执行容器中的命令, 可以执行多次
  • CMD 是当容器启动时执行的命令,且Docker中只能使用一次

编写shell文件

#!/bin/bash
cd /usr/src
scrapy crawl guoxue

构建镜像

在执行docker build之前,需要将sh文件改为可执行, 且文件格式改为UNIX。

vi run.sh文件,按shift+:进入命令行,执行以下指令:

:set ff=unix
docker build -t dushu:1.0 .

成功之后,查看镜像是否存在

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

网络安全面试题

绿盟护网行动

还有大家最喜欢的黑客技术

网络安全源码合集+工具包

所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-HpqUn6BO-1713480010848)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值