爬虫知识点总结

爬虫总结

一、

1.什么是爬虫?

爬虫就是:模拟浏览器发送请求,获取响应

2.爬虫的分类,爬虫的流程

  • 聚焦爬虫:针对特定的网站的爬虫
    • 准备url地址 -->发送请求 获取响应–> 提取数据–> 保存
    • 获取响应–> 提取url地址,继续请求
  • 通用爬虫:搜索引擎的爬虫
    • 抓取网页–> 数据存储–>预处理–> 提供检索服务,网站排名

3.浏览器发送请求的过程

  • 爬虫请求的:url地址对应的响应
  • 浏览器获取到的内容:elements的内容=url对应的响应+js+css+图片
  • 爬虫获取的内容和elements内容不一样,进行数据提取的时候,需要根据url地址对应的响应为准进行数据的提取

4.http的请求头user-agent有什么用

  • user-agent:告诉对方服务器是什么客户端正在请求资源,爬虫中模拟浏览器非常重要的一个手段
  • 爬虫中通过把user-agent设置为浏览器的user-agent,能够达到模拟浏览器的效果
  • cookie:获取登录只有才能够访问的资源

5.利用requests模板如何发送请求和获取响应

  • response = requests.get(url)
  • response.text -> str # 根据响应信息进行有规律的推测网页的编码
    • response.encoding=“utf-8”
    • response.encoding=”gbk”
  • response.content -> bytes
    • response.content.decode(“utf8”)
      # 发送请求,获取响应
      def parse(self, url, data):
      response = requests.get(url,params=params,headers=self.headers)
      response = requests.post(url,data=data, headers=self.headers)
      return response.content.decode()
  • response.status_code
  • response.request.headers
  • response.headers
  • 一般来说名词,往往都是对象的属性,对应的动词是对象的方法
  • 获取网页源码的通用方式:
    1. response.content.decode() 的方式获取响应的html页面
    2. response.content.decode(“GBK”)
    3. response.text

6.python2和python3中的字符串

ascii 一个字节表示一个字符
unicode 两个字节表示一个字符
utf-8 边长的编码方式,1,2,3字节表示一个字符

  • python2
    • 字节类型:str,字节类型,通过decode()转化为unicode类型
    • unicode类型:unicode ,通过encode转化为str字节类型
  • python3
    • str:字符串类型,通过encode() 转化为bytes
    • bytes:字节类型,通过decode()转化为str类型

7.常见的状态响应码

  • 200:成功
  • 302:临时转移至新的url
  • 307:临时转移至新的url
  • 404:not found
  • 500:服务器内部错误

二、

1.requests中headers如何使用,如何发送带headers的请求

  • 模拟浏览器,欺骗服务器,获取和浏览器一致的内容
  • headers = {“User-Agent”:“从浏览器中复制”}
  • headers = {
    “Origin”: “http://ntlias-stu.boxuegu.com”,
    “Referer”: “http://ntlias-stu.boxuegu.com/”,
    “User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.17 Safari/537.36”
    }
  • requests.get(url,headers=headers)

2.发送带参数的请求

params = {"":""}
url_temp = “不完整的URL地址”
requests.get(url_temp,params=params)

3.requests如何发送post请求

data = {“从浏览器中form data的位置寻找”}
requests.post(url,data=data)

4.requests中如何使用代理,使用代理的目的,代理的分类

  • proxies = {“https”: “https://117.127.0.195:8080”}
  • proxies = {协议:协议+ip+端口}
  • requests.get(url,proxies=proxies)
    目的:
    • 反反爬
    • 隐藏真实ip

代理的分类

  • 高匿名代理:不知道在使用代理
  • 匿名代理:知道在使用代理,但是不知道真实ip
  • 透明代理(Transparent Proxy):对方知道真实的ip

5.requests中session类如何使用,为什么要使用session

  • session = requests.Session()
  • session.post(url,data) #cookie会保存在session中
  • session.get(url) #用session发送请求会带上之前的cookie
  • 注意:这块的session类和之前所学的session无任何关系

6.列表推导式

In [41]: [i for i in range(10)]
Out[41]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [42]: [i/2 for i in range(10)]
Out[42]: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
In [43]: [i/2 for i in range(10) if i%2==0]

三、

1.requests进行携带cookie登录

  • cookie字符串放在headers中
  • 把cookie字典交给requests请求方法的cookies

2.寻找登录接口的方法

  • form表单action对应的url地址
    • 用户名和密码的input标签中,name的值作为键,用户名和密码作为值的字典,作为post data
  • 通过抓包,定位url地址
    • form data

3.分析js,获取加密的数据

  • 观察变化
  • 定位js
    • 通过event listener定位js的位置
    • 通过搜索url地址中的关键字,通过chrome的search all file来进行搜索
  • 进行分析
    • 通过添加断点的方式分析js
  • 执行js
    • 完全的使用python模拟js的执行过程

4.requests处理ssl证书

requests.get(url,verify=False)

5.获取响应中的cookie,转化为字典

  • response = requests.get(url,headers=headers)
  • requests.utils.dict_from_cookiejar(response.cookies)

6.requests中超时参数的使用,retrying模块的使用

  • from retrying import retry
  • requests.get(url,timeout=3)
  • 通过装饰器的方式使用retry,进行异常捕获,重新执行被装饰的函数
    from retrying import retry
    @retry(stop_max_attempt_number=3)
    def fun():
    pass

7.数据的分类

  • 结构化数据 json, xml
    • 直接使用模块转化为python类型
  • 非结构化数据 html
    • re,xpath

8.json模块的使用

  • 数据交换格式
  • json.loads(json_str) json字符串转化为python类型
  • json.dumps(python_type,ensure_ascii=False,indent=2) python类型转化为json字符串
  • json.load() 把包含json的类文件对象中的数据提取出来转化为python类型
  • json.dump() python类型存入类文件对象中
  • 那么对于为什么需要模拟登陆?
    获取cookie,能够爬取登陆后的页面

9.requests模拟登陆的三种方法

  • session
    • 实例化对象
    • session.get(url) #cookie保存在session中
    • session.get(url) #带上保存在session中cookie
  • cookie方法headers中
  • cookie传递给cookies参数
    • cookie = {“cookie 的name的值”:“cookie 的value对应的值”}

10.三元运算符

a = 10 if  3<2 else 100

11.字典推导式

In [8]: {i:i+10 for i in range(10)}
Out[8]: {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17, 8: 18, 9: 19}

In [9]: {i:i+10 for i in range(10) if i%2==0}
Out[9]: {0: 10, 2: 12, 4: 14, 6: 16, 8: 18}

四、

1.正则的语法

  • 字符
    • . 能够匹配\n之外的所有字符 re.S模式下可以匹配\n
    • \ 转义
    • [] 或的效果,从中选择一个, [abc]+ 能够匹配多个
    • | 或的效果
  • 预定义的字符集
    • \d 数字
    • \s 空白字符串,包含空格、\n,\t
    • \w 单词字符,a-zA-Z0-9_
  • 数量词
      • 匹配0次或者多次
      • 能够匹配1次或者多次
    • ? 能够匹配0-1次,让*,+非贪婪

2.re模块的常用方法

  • re.findall(“正则表达式regex”,“待匹配的字符串”) # 返回列表,或者是空列表
  • re.sub(“regex”,"_",“待替换的字符串”) # 返回字符串
  • p = re.compile(“regex”,re.S/re.DOTALL) # 返回一个p模型,编译,提高匹配效率
    • p.findall(“待匹配的字符串”)
    • p.sub("_",“待替换的字符串”)

注:re.S 匹配\n re.DOTALL 匹配Tab键

3.原始字符串r

  • 定义:相对于特殊符号而言,表示特殊符号的字面意思
  • 用途:
    • 正则中,能够忽略转义符号带来的影响,待匹配的字符串中有几个\,正则表达式中加上r,照着几个\即可
    • windows文件路径

4.xpath语法

  • xpath的安装 pip install lxml
  • // 的用途
    • //a html中所有的a
    • div//a div中所有的a,包括div下的后代节点中的a
    • a//text() a下的所有的文本
  • @ 的使用
    • a/@href 获取a的href的值
    • //a[@class=‘b’]
  • text() 的使用
    • //a/text() 获取所有的a下的文本
    • //a[text()=‘下一页’] 获取文本为下一页的a标签
    • a//text() a下的所有的文本
  • xpath包含的语法
    • //div[contains(@class,“i”)] class包含i的div标签
    • //a[contains(text(),“下一页”)] 文本包含下一页的a标签
  • 兄弟标签
    • /a/follow-sibling::ul[1]
  • xpath 选择特定位置
    • //a[1] 第一个
    • //a[last()] 最后一个
    • //a[last()-1] 倒数第二个
    • //a[postion()<4] 前三个

5.lxml模块的使用

from lxml import etree
element = etree.HTML(bytes/str) #返回element
ret_list = element.xpath("xpath字符串") #返回列表
bytes = etree.tostring(element) #返回bytes类型字符串
#数据提取时:先分组,再提取

五、

1.xpath包含的语法

//div[contains(@class,“i”)] class包含i的div标签
//a[contains(text(),“下一页”)] 文本包含下一页的a标签

2.url地址解码的方法

  • requests.utils.unquote(url)

3.准备url地址

  • 知道url地址的规律,知道一共多少页,准备url列表,果壳,糗百
  • 不知道url地址规律,或者不知道一共多少页,准备start_url ,贴吧

4.多线程爬虫

  • threading
    • t1 = threading.Thread(targe=func,args=(,))
    • t1.setDaemon(True) #设置为守护线程
    • t1.start() #此时线程才会启动
  • 队列
    • from queue import Queue
    • q = Queue()
    • q.put() 队列计数+1
    • q.get() 队列计数不会-1
    • q.task_done() 和get()方法配合,队列计数-1
    • q.join() #阻塞主线程,让主线程等待队列任务结束之后在结束,队列任务在计数为0时技术

5.多进程爬虫

  • multiprocessing
    • p = multiprocessing.Process(trage=func,args=(,))
    • p.daemon = True #设置为守护进程,主线程结束,子进程结束
    • p.start()
  • from multiprocessing import JoinableQueue
    • q = JoinableQueue()
    • q.join() # 让主进程阻塞,等待队列任务计数,计数为0队列任务结束,
    • q.put() # 计数+1
    • q.get() # 计数不会-1
    • q.task_done() # get和task_done一起使用才会减一

6.线程池和协程池的使用

  • 线程池
    • from multiprocessing.dummy import Pool
    • pool = Pool(5)
    • pool.apply_async(func,callback=func2)
  • 协程池
    • import gevent.monkey
    • gevent.monkey.patch_all()
    • from gevent.pool import Pool
    • pool = Pool(5)
    • pool.apply_async(func,callback=func2)

六、

1.安装driver

  • chromdriver 需要对应chrome版本
  • 提示权限不足,sudo chmod +x phantomjs
  • chromdriver --version
  • phantomjs --version

2.selenium如何使用

  • 功能:请求页面,提取数据,开启隐形的浏览器,能够执行其中的js,可获取cookie

    from selenium import webdriver
    driver = webdriver.PhantomJS() # 没有界面,不建议使用
    driver = webdriver.Chrome() # 带界面
    driver.get_cookie(‘name’) # 获取cookie值,需要传name
    driver.get_cookies() # 获取cookie
    driver.get(url) #发送请求
    driver.quit()

3.selenium定位元素的方法

  • driver.find_element #返回第一个元素,如果没有报错
  • driver.find_elements 返回包含元素的列表,没有就是空列表
  • driver.find_elements_by_xpath() # 只能定位到标签(即元素), 不能定位到文本值和属性值
  • driver.find_elements_class_name() #定位class属性
  • driver.find_elements_by_id() #定位id属性
  • driver.element.text #获取文本
  • driver.element.get_attribute(“textContent”) #获取隐藏元素的文本
  • driver.element.get_attribute(“href”) #元素获取属性值

4.selenium如何处理frame

  • driver.switch_to.frame(id,name,element)

5.验证码的识别

  • url地址不变,验证码不变
    • 请求验证码的地址,获取响应,进行识别
  • url地址不变,验证码变化
    • 请求验证码,发送登录请求,需要带上统一套cookie,才能够都能路成功,对应可以使用requests.Session()来实现
  • selenium处理验证码
    • 带上selenium的driver中的cookie来请求验证码
    • selenium截屏,获取验证

6.mongodb的服务端和客户端启动方法

  • 服务端启动
    • sudo service mongod start
    • sudo mongod --config /ect/mongod.conf &
  • 客户端启动
    • mongo

7.mongodb中数据库的方法

  • 数据库可以不需要提前创建,使用use一个不存在的数据库即可创建
  • use db_name 使用数据库 数据库可以不存在
  • db 查看当前所在的数据库
  • show dbs /show databases 查看所有的数据库
  • db.dropDatabase() 删除数据库
  • 数据库名.dropDatabase() #删除数据库

8.mongodb中集合的方法

  • 集合不需要提前创建,插入数据的时候自动创建
  • show collections #查看所有的集合
  • db.集合名.drop() #删除集合
  • db.集合名.find() #集合的使用

9.mongodb的增删改查的方法

  • 插入insert
    • insert() 插入数据,_id相同会报错
    • save() 保存数据_id相同会更新,不存在会插入
  • 删除remove
    • db.col_name.remove({条件},{justOne:flase}) #默认删除全部满足条件的内容
    • db.col_name.remove({条件},{justOne:ture}) 删除一条满足条件的内容
  • 更新update
    • update 更改时,找到满足条件时,除id之外全部覆盖
    • db.collection.update({条件},{$set:{name:10086}},{multi:true})
      #默认更新一条,multi为true会更新全部
    • db.col_name.update({条件},{name:1}) #会把满足条件的数据的第一条更新为{name:1}
    • db.col_name.update({条件},{$set:{name:1}})
      • 把满足条件的第一条的name值更新成1
    • db.col_name.update({条件},{$set:{name:1}},{multi:true})

七、

1.mongodb在pycharm中的增删改查

stu.insert({"name": "张三", "age": 12})  # 增
stu.remove({"age": 12}, multi=True)    # 删
stu.update({"age": 12}, {"$set": {"name": "李斯"}}, multi=True)  # 改
for data in stu.find():  # 查
     print(data)

2.mongodb的运算符

- 比较运算符
  - $gt 大于
  - $lt 小于
  - $gte 大于等于
  - $lte 小于等于
  - $ne 不等
- 逻辑运算符
  - and  {age:"",name:""}
  - or {$or:[{条件1},{条件2}]}
- 范围运算符
  - $in db.col.find({age:{$in:[18,19,30]}})
  - $nin 不在范围内

3.mongodb中的计数,去重,排序

- 计数
  - db.col.count({条件})
  - db.col.find({条件}).count()
- 去重
  - db.col.distinct("字段",{条件})
- 排序
  - db.col.find().sort({})
- 投影
  - 指定数据内容的字段
  - db.stu.find({条件},{name:1,_id:0})
  - 返回的数据中只会包含name字段,_id不会显示

4.mongodb聚合中$group的使用

  • 分组

    db.stu.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"hometown",count:{KaTeX parse error: Expected 'EOF', got '}' at position 6: sum:1}̲,total_age:{sum:“KaTeX parse error: Expected 'EOF', got '}' at position 5: age"}̲,avg_age:{avg:”$age"}}}
    )

  • _id分组的依据

  • $age 取age对应的值

  • $sum:1 把每条数据作为1进行统计,统计的是个数

  • s u m : " sum:" sum:"age" 统计年龄对应的和

  • $group对应的字典中的键是输出数据的键

  • 不分组

    db.stu.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: …id:null,count:{sum:1}}}
    )

    按照一个字段分组
    db.col.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"gender",count:{KaTeX parse error: Expected 'EOF', got '}' at position 6: sum:1}̲}} ) 按照多个…group:{_id:{gender:“ g e n d e r " , h o m e t o w n : " gender",hometown:" gender",hometown:"hometown”},count:{KaTeX parse error: Expected 'EOF', got '}' at position 6: sum:1}̲}} ) 不分组,…group:{_id:null,count:{$sum:1}}}
    )
    KaTeX parse error: Expected '}', got 'EOF' at end of input: …ggregate( {group:{_id:“KaTeX parse error: Expected '}', got 'EOF' at end of input: gender",name:{push:”$name"}}}
    )

4.统计整个文档

  • 数据透视
    把不同行的数据,放到一行来展示

    db.stu.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"gender",name:{ p u s h : " push:" push:"name"},hometown:{ p u s h : " push:" push:"hometown"}}}
    )

  • 按照多个字段进行分组
    按照多个字端进行分组,_id的值是一个json

    db.stu.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: …_id:{hometown:"hometown",gender:"KaTeX parse error: Expected 'EOF', got '}' at position 8: gender"}̲,count:{sum:1}}}
    )

  • 多字段分组练习
    当某个键对应的值是字典的时候,取其中的值需要使用.操作,$_id.country表示取到_id这个字典下的country的键对应的值

  • 第一条作为第二条的管道进行查找,计数

    db.tv3.aggregate(
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: …{_id:{country:"country",province:“ p r o v i n c e " , u s e r i d : " province",userid:" province",userid:"userid”}}},
    {KaTeX parse error: Expected '}', got 'EOF' at end of input: …{_id:{country:"_id.country",province:"KaTeX parse error: Expected 'EOF', got '}' at position 14: _id.province"}̲,count:{sum:1}}}
    )

5.mongodb中$match

过滤

db.col.aggregate(
  {$match:{age:{$gt:18}}}
    )

6.mongodb中$project

投影,修改文档的输入输出结构

db.stu.aggregate(
  {$group:{_id:"$hometown",count:{$sum:1}}},
  {$project:{_id:0,sum:"$count",hometown:"$_id"}}
  )
db.tv3.aggregate(
  {$group:{_id:{country:"$country",province:"$province",userid:"$userid"}}},
  {$group:{_id:{country:"$_id.country",province:"$_id.province"},count:{$sum:1}}},
  {$project:{country:"$_id.country",province:"$_id.province",counter:"$count",_id:0}}
  )

7.limit $sort

db.stu.aggregate(
  {$group:{_id:"$hometown",count:{$sum:1}}},
  {$sort:{count:-1}},
  {$skip:1},
  {$limit:2}
  )

8.mongodb索引

  • 创建索引
    • db.col.ensureIndex({name:1})
    • db.col.createIndex()
  • 查看索引
    • db.col.getIndexes()
  • 删除索引
    • db.col.dropIndex({name:1})
  • 建立联合索引
    • db.col.ensureIndex({name:1,age:-1})
  • 建立唯一索引
    • db.col.ensureIndex({name:1},{unique:true})

9.mongodb备份和恢复

  • 备份
    • mongodump -h host -d database -o output_path
  • 恢复
    • mongorestore -h host -d database --dir 恢复的路径

10.pymongo的使用

from pymongo import MongoClient

#实例化client
client = MongoClient(host,port)  
#选择集合
collection = client["db"]["collection"]

#查询
collection.find() #返回全部的数据,返回cursor对象,只能获取其中内容一次
collection.find_one() #返回一条

#插入
collection.isnert_one()
collection.insert_many()

#更新
collection.update_one({name:"a"},{"$set":{"name":"noob"}})
collection.update_many()

#删除
collection.delete_one()
collection.delete_many()

八、

1.scrapy框架安装

  • pip install Twirted.whl(本地)
  • pip install pywin32
  • pip install scrapy

2.scrapy 的数据传递的流程

  • 五大组件
    • 调度器:存储请求队列
    • 下载器:根据request发情请求获得响应response
    • 爬虫:提取url转为request,提取数据
    • 管道:数据清洗和数据保存
    • 引擎:负责连接其他四个组件(之间互不相通),保证数据的传递
  • 中间件 只能处理request和response
    • 爬虫中间件
    • 下载器中间件
  • 运行过程
    0.调用start_requests()方法,将start_urls中所有的url构造成request对象,并放入调度器
    1.调度器取一个request -> 引擎 -> 下载器中间件 -> 下载器
    2.下载器根据request下载得到response -> 下载器中间件 -> 引擎 -> 爬虫中间件 -> 爬虫
    3.爬虫提取数据
    3.1 爬虫提取url转为request -> 爬虫中间件 -> 引擎 -> 调度器
    3.2 爬虫提取数据(item) -> 引擎 -> 管道
    4.管道实现数据的处理和保存

3.scrapy爬虫项目的创建

  • 创建项目
    scrapy startproject myspider
  • 创建爬虫
    cd myspider
    scrapy genspider spider_name allowed_domain
  • 运行
    scrapy crawl spider_name
  • 构造一个request请求
    yield scrapy.Request(url,callback,meta,dont_filter) # url需要手动补全
    callback: 将来url响应的处理函数
    meta:数据不完整时,传递当前数据到下一个响应函数
    dont_filter:默认False,即过滤,过滤的情况下,不会重复发起相同的url请求
    response.follow(url) # url不用补全,会根据response.url自动补全
  • 完善爬虫 :提取数据,提取url地址组成request
  • 完善管道:数据的处理和保存
  • yield 能够yield None,item对象,字典,或者请求,不能够yield一个列表

4.完善spider

  • parse方法必须有,用来处理start_urls对应的响应的
  • extract() response.xpath() 从中提取数据的方法,没有就返回一个空列表
  • extract_first() response.xpath() 的结果中提取第一个字符串的方法,没有返回None值

5.完善管道

  • 管道需要在settings中开启,添加管道的路径,对应的键:管道的位置,值表示的是管道距离引擎的远近,数字越小,优先级越高,越先经过

  • process_item(item,spider)方法必须有 ,spider表示的是传递item过来的爬虫实例

  • 从爬虫中通过yield 把数据交给引擎传递给pipeline,只能是Request, BaseItem, dict or None

    class YangguangPipeline(object):
    def process_item(self, item, spider):
    处理从spider发来的item数据

    需要在配置文件中开启后才能生效
    ITEM_PIPELINES = {
    ‘yangguang.pipelines.YangguangPipeline’: 300, # 300标识数据处理的优先级,数字越低,优先级越高}

6.数据提取url地址补全

  • 手动字符串相加
  • urllib.parse.urljoin(baseurl,url)
    • 后面的url会根据baseurl进行url地址的拼接
      • import urllib
      • url1 = “position.php?&start=2890#a”
      • url2 = “https://hr.tencent.com/position.php?&start=3580#a”
      • urllib.parse.urljoin(url2, url1)
      • 输出:“https://hr.tencent.com/position.php?&start=2890#a”
      • next_url = urllib.parse.urljoin(response.url, next_url) # response.url可以获取完整的url字符串,是response的一个属性
  • response.follow(url,callback)
    • 能够根据response的地址把url拼接完整,构造成Request对象请求

7.scrapy如何构造请求

  • scrapy.Request(url,callback,meta,dont_filter)
    • url:详情页,下一页的url
    • callback:url地址响应的处理函数
    • meta:在不同的函数中传递数据
    • dont_filter ::默认是false表示过滤,scrapy请求过的url地址,在当前的运行过程中不会继续被请求,如果需要继续被请求,可以把dont_filter=True
  • yield scrapy.Request(url,callback,meta,dont_filter)

8.scrapy的Item如何使用

#定义
class Item(scrapy.Item):
  name = scrapy.Field()  

#使用
导入。使用name字典

9.scrapy中parse函数是做什么的

  • 处理start_urls中的url地址的响应

九、

1.scrapy shell 如何使用,能干什么

  • scrapy shell url 能够进入交互式终端
  • 查看scrapy中模块的属性
  • 测试xpath

2.response对象有哪些常见属性

  • response.body 能够后去响应bytes字符串
  • response.url
  • response.request.url
  • response.headers
  • response.request.headers
  • resposne.text 能够后去响应str字符串

3.open_spider 和close_spider 在管道里面配置

  • open_spider(spider) #能够在爬虫开启的时候执行一次
  • close_spdier(spider) #能够在爬虫关闭的时候执行一次
  • 在和数据库建立连接和断开连接的时候使用上述方法

4.deepcopy的使用,苏宁代码中为什么需要deepcopy

  • a = deepcopy(b) #强制传值
  • 苏宁代码数据重复
    • scrapy中的内容是异步执行的,解析函数可能同时在执行,操作的同一个item,
    • 大分类下的所有的图书用的是一个item字典

十、

1.crwalspider如何创建爬虫

  • scrapy genspider -t crawl 爬虫名 语序允许爬取的范围

2.crwalspdier中rules的编写

  • rules 元组,元素是Rule
  • Rule(LinkExtractor(allow=“正则”),follow=True,callback=“str”)
    • LinkExtractor:传入正则匹配url地址
    • follow:为True表示提取出来的响应还会经过rules中的规则进行url地址的提取
    • callback:表示提取出来的响应还会经过callback处理

3.crwalspider中不同的解析函数间如何传递数据,如果不能,应该如何操作?

  • 在前一个Rule的callback中实现手动构造请求

    yield scrapy.Request(url,callback,meta)

4.下载器中间件如何使用

使用代理

  • request.meta[‘proxy’] = “协议+ip+端口”

  • 在settings中开启中间件
    SPIDER_MIDDLEWARES = {
    ‘book.middlewares.BookSpiderMiddleware’: 543,
    }
    DOWNLOADER_MIDDLEWARES = {
    ‘book.middlewares.BookDownloaderMiddleware’: 543,
    }

    class TestMid:
    def process_request(self,request,spdier):
    #处理请求
    request.headers[“User-Agent”] = “” #使用ua
    request.meta[“proxy”] = “协议+ip+端口”
    return None #请求会继续后面处理
    return Request #把请求交给调度器
    return Response #把响应交给爬虫

    def process_response(self,request,response,spider):
      #处理响应
      return response #把响应交给爬虫
      return request #把请求交给调度器
    

5.模拟登陆的三种方式

  • 携带cookie进行登录
    • yield scrapy.Request(url,callback=self.parse,cookies={},meta={})
  • 发送post请求
    • yield scrapy.FormReuqest(url,formdata={请求体},callback=self.parse_login)
  • 表单提交
    • yield scrapy.FormReuqest.from_response(response,formdata={},callback=self.parse_login)

6.正则比倒是忽略大小写

  • re.I/re.IGNORECASE

十一、

  1. scrapy_redis

scrapy_redis 是scrapy框架的一个扩展组件,实现了两个功能:

  • 增量式爬虫
  • 分布式爬虫

实质:就是将请求队列和指纹集合进行了持久化存储

在seeeting.py中继续配置

# 指定了去重的类
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 制定了调度器的类
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 调度器的内容是否持久化
SCHEDULER_PERSIST = True
REDIS_URL = "redis://127.0.0.1:6379"

注:scrapy_redis 只是scrapy框架的一个组件

2.scrapy_redis为什么能够实现去重和请求的持久化以及分布式爬虫

  • scrapy_redis把指纹和request对象存储在了redis,下一次程序启动起来之后会从之前的redis中读取数据
  • 实现分布式:过个服务器上的爬虫公用一个redis

3.scray_redis中dmoz给我们展示了一个什么样的爬虫

  • 增量式的爬虫:基于request对象的增量
  • 分布式爬虫
  • 主要体现在 请求队列和指纹集合的持久化存储

4.scrapy_redis中如何实现一个增量式的爬虫

  • settings中进行配置,指定去重的类,调度器的类,添加上redis_url
    DUPEFILTER_CLASS = “scrapy_redis.dupefilter.RFPDupeFilter” # 指定去重的类
    SCHEDULER = “scrapy_redis.scheduler.Scheduler” # 调度器的类
    SCHEDULER_PERSIST = True # 指定调度器的内容是否持久化
    REDIS_URL = “redis://127.0.0.1:6379” # 添加redis_url地址

5.scrapy中如何生成的指纹,有什么启发

使用sha1加密请求的url地址,请求的方法和请求体,得到16进制字符串作为指纹 每个指纹40位

fp = hashlib.sha1()   # sha1加密
fp.update(to_bytes(request.method))  # 请求方法
fp.update(to_bytes(canonicalize_url(request.url)))  # 请求地址
fp.update(request.body or b'')  # 请求体

cache[include_headers] = fp.hexdigest()  #生成指纹  加密之后的16进制字符串

6.scrapy_redis什么情况下request会入队

def enqueue_request(self, request):
    if not request.dont_filter and self.df.request_seen(request):
        self.df.log(request, self.spider)
        return False
    self.queue.push(request)
    return True
  • 全新的request对象,之前没有见过的request
  • dont_filter = True 不过滤,请求过的url地址让他继续请求
  • start_urls中的url地址能够反复入队请求,因为默认是不过滤的

7.要查看数据是否存在于redis的集合中,如果不存在就插入

added = self.server.sadd(self.key, fp)
#added= 0 表示存在
#added!=0 表示不存在,并且已经插入
return added == 0

8.关于对象的序列化和反序列化

request = Request("http://www.baidu.com", meta={"item": {"hello": "wrold"}}, parse="parse_book")
  • 对象的序列化
    data = pickle.dumps(request)
    print(data)
  • 反序列化
    req = pickle.loads(data)
    print(req.meta)

    print(id(request), id(req))
  1. 如何去重

1.请求生成指纹

fp = hashlib.sha1()
fp.update(to_bytes(request.method))
fp.update(to_bytes(canonicalize_url(request.url)))
fp.update(request.body or b'')
return fp.hexdigest()

利用hashlib的sha1,对request的请求体、请求url、请求方法进行加密,返回一个40位长度的16进制的字符串,称为指纹

  1. 进队

    def enqueue_request(self, request):
    if not request.dont_filter and self.df.request_seen(request):
    self.df.log(request, self.spider)
    return False
    self.queue.push(request)
    return True

  2. 如果请求需要过滤,并且当前请求的指纹已经在指纹集合中存在了,就不能进入队列了

  3. 如果不需要过滤,直接进入队列

  4. 如果请求需要过滤,并且请求的指纹是一个新的指纹,进入队列

10.数据去重

  • 中间件去重
    process_response(request,response,spider):
    #set可以是内存set集合,也可以是redis的set
    ret = set.add(md5(response.body))
    if ret == 0:
    return request
    else
    return response
  • 建立复合索引
    # 复合索引,加速和去重
    stu.ensure_index([(“hometown”, 1), (“age”, 1)], unique=True)
    # 根据数据的特征,在mongodb中 对指定字段建立复合索引,所有字段值相同时就无法二次插入了
  • 布隆过滤器

11.哈希函数的特性:

  1. 输入域无限,输出域有限
  2. 相同的输入 必然 得到 相同的输出 (不是随机性)
  3. 不同的输入 也可能 得到 相同的输出 (哈希碰撞)
  4. 离散性,对于输出域中的每个结果,在整个输出域是均分分布的。
  5. dict 字典 -> 哈希表

十二、

1.Redisspider的爬虫和scrapy.spider的区别

实现Redisspider 分布式爬虫,请求的持久化,去重的持久化

  • 区别
    • 父类不一样,RedisSpider继承的父类是RedisSpider
    • RedisSpider没有start_url, 多了redis_key ,往redis_key存入start_url地址
      • redis_key表示redis中存放start_url地址的键
    • settings 中多了几行配置
  • 创建爬虫
    • scrapy genspider 爬虫名 爬取范围
    • 修改父类名
    • 修改redis_key
  • 启动爬虫
    • 让爬虫就绪:scrapy crawl 爬虫名
    • redis中存入url地址:lpush redis_key url

2.RedisCrawlSpider的爬虫和crwalspdier的区别

实现RedisCrawlSpider 分布式爬虫,请求的持久化,去重的持久化

  • 区别
    • RedisCrawlSpider继承的父类是RedisCrawlSpider
    • RedisCrawlSpider没有start_url,多了redis_key ,往redis_key存入start_url地址
      • redis_key表示redis中存放start_url地址的键
    • settings 中多了几行配置
  • 创建爬虫
    • scrapy genspider -t crawl 爬虫名 允许爬取的范围
    • 修改父类名
    • 添加redis_key
  • 启动爬虫
    • 让爬虫就绪 scrapy crawl 爬虫
    • lpush redis_key url 爬虫会启动

3.crontab使用的方法

  • 分钟 小时 日 月 星期 命令
  • 30 9 8 * * ls #每个月的8号的9:30执行ls命令

在爬虫中使用crontab

    1. 爬虫启动命令写入脚本文件

      cd dirname $0
      scrapy crawl 爬虫名 >> run.log 2>&1

    1. 给脚本添加可执行权限
    • chmod +x run.sh
    1. 把脚本文件添加到crontab的配置中
    • 30 6 * * * /home/python/myspider/run.sh

十三、

1.什么是框架,为什么需要开发框架

  • 框架:为了解决一类问题而开发的程序,能够提高开发效率
  • 第三方的框架不能够满足需求,在特定场景下使用,能够满足特定需求

2.scrapy_plus中有哪些内置对象和核心模块

  • core
    • engine
    • scheduler
    • downloader
    • pipeline
    • spider
  • http
    • request
    • response
  • middlewares
    • downloader_middlewares
    • spider_middlewares
  • item

3.说出scrapy_plus实现引擎的基础逻辑

  • 1.调用爬虫的start_request方法,获取start_request请求对象
  • 调用爬虫中间件的process_request方法,传入start-request,返回start_request
  • 2.调用调度器的add_request,传入start_request
  • 3.调用调度器的get_request方法,获取请求
  • 调用下载器中间件的process_request,传入请求,返回请求
  • 4.调用下载器的get_response方法,传入请求,返回response
  • 调用下载器中间件的process_response方法,传入response,返回response
  • 调用爬虫中间件的process_response方法,传入response,返回response
  • 5.调用spider的parse方法,传入resposne,得到结果
  • 调用爬虫中间件的process_request方法,传入request,返回request
  • 6.判断结果的类型,如果是请求对象,调用调度器的add_request,传入请求对象
  • 7.否则调用管道的process_item方法,传入结果

4.如何在项目文件中添加配置文件能够覆盖父类的默认配置

  • 1.在框架中conf文件夹下,建立default_settings,设置默认配置
  • 2.在框架的conf文件夹下,建立settings文件,导入default_settings中的配置
  • 3.在项目的文件夹下,创建settings文件,设置用户配置
  • 4.在框架的conf文件夹下的settings中,导入settings中的配置,会覆盖框架中的默认配置

十四、

1.getattr如何使用?

  • getattr 通过传入字符串,获取python对象或者是方法
  • 现在有test(),知道test中间有个方法名叫做func
    • getattr(test(),“func”) #返回test.func()

2.importlib如何使用?

  • 能够动态的导入模块
    import importlib
    module = importlib.import_module(“模块的位置”)
    cls = getattr(module,“Test”) #获取模块下的类
    func = getattr(cls(),“func”) #获取cls中的func方法

爬虫项目

  • 公司名字:

项目

  • 运行环境
    • linux+pycharm+redis+scrapy+mysql+mongodb+scrapy_redsi+selenium
  • 项目描述
    • 抓取了(多个)网站,获取了数据,解决了***需求,使用的技能
    • request+selenium
    • selenium + scrapy
    • scrapy_redsi
    • 自己实现的框架完成了一个项目
  • 个人职责
    • 使用的是技能
      • request :发送请求
      • selenium:
        • 获取动态html页面,数据提取更方便
        • 专门进行登录,获取cookie,组成cookie池,其他程序从cookie池中获取cookie,请求登陆之后的页
      • scrapy:
        • 为了加快抓取速度,使用了scrapy
      • scrapy_redis
        • 为了试下增量式爬虫,使用了scrapy_redis
        • 为了实现分布式,使用了scrapy_redis
        • 为了实现持久化的去重,
      • 自己实现的框架完成了一个项目
        • 框架的实现逻辑
        • 实现去重的方式
        • 分布式的实现方式
        • 持久化的实现
    • 反扒
      • js生成的数据
        • 使用selenium配合无头浏览器
        • 分析了js,看到了js的实现过程,python实现了一遍
        • js2py的工具执行了js
      • 验证码
        • 使用了打码平台
      • 代理ip
        • 对方服务器有通过ip进行限速,购买了代理ip组成了ip池,通过一个程序,判断ip的可用性
    • 去重
      • 基于url地址去重
        • sha1加密了以写请求的字典
      • 基于数据的去重
        • sha1加密了数据中的某些字典,得到指纹,存在redis中的集合中进行对数据的去重
        • 在数据库中建立联合索引进行去重

你可能感兴趣的:(爬虫)