爬虫系列-自动给抽屉点赞、爬取cnblogs、scrapy的请求传参、提升scrapy爬取效率、下载中间件、selenium的使用、去重规则、分布式爬虫、破解知乎登陆(js逆向和解密)、反扒措施

知识回顾

# 1 scrapy:5大组件,2大中间件
# 2 安装:pip install scrapy   (twisted装不上,pywin32安装包)
# 3 创建项目 scrapy startproject 项目名
# 4 创建爬虫:scrapy genspider 爬虫名字 爬取的网站
# 5 目录介绍
# 6 爬虫启动:
	-scrapy crawl 爬虫名字
    -项目路径下建一个py文件
    	from scrapy.cmdline import execute
        execute(['scrapy','crawl','chouti','--nolog'])
# 6 配置文件:三个参数
# 7 持久化:两种方案
	-scrapy crawl 爬虫名字 -o 文件名.csv  (解析函数返回值必须是列表套字典)
    -pipline持久化:
    	-在Item中写个类(存的字段)
        -在爬虫的解析中,实例化,并赋值(字典赋值)
        -yield 对象
        -在pipline中写持久化的类:open_spider,close_spider,process_item(一定要返回,后续才能继续走)
        -在配置文件中配置pipline(数字越小,优先级越高)

建议查看: Scrapy框架 密码:xiaoyuanqujing@666

自动给抽屉点赞

from selenium import webdriver
import time
import requests

bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.implicitly_wait(10)
bro.get('https://dig.chouti.com/')

login_b=bro.find_element_by_id('login_btn')
print(login_b)
login_b.click()

username=bro.find_element_by_name('phone')
username.send_keys('18953675221')
password=bro.find_element_by_name('password')
password.send_keys('lqz123')

button=bro.find_element_by_css_selector('button.login-btn')
button.click()
# 可能有验证码,手动操作一下
time.sleep(10)


my_cookie=bro.get_cookies()  # 列表
print(my_cookie)
bro.close()

# 这个cookie不是一个字典,不能直接给requests使用,需要转一下
cookie={}
for item in my_cookie:
    cookie[item['name']]=item['value']


headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
    'Referer': 'https://dig.chouti.com/'}
# ret = requests.get('https://dig.chouti.com/',headers=headers)
# print(ret.text)


ret=requests.get('https://dig.chouti.com/top/24hr?_=1596677637670',headers=headers)
print(ret.json())
ll=[]
for item in ret.json()['data']:
    ll.append(item['id'])

print(ll)
for id in ll:
    ret=requests.post(' https://dig.chouti.com/link/vote',headers=headers,cookies=cookie,data={'linkId':id})
    print(ret.text)

'https://dig.chouti.com/comments/create'
'''
content: 说的号
linkId: 29829529
parentId: 0

'''

全站爬取cnblogs

已经失效

# cnblog文件
import scrapy

from cnblogs.items import CnblogsItem
from scrapy import Request

class CnblogSpider(scrapy.Spider):
    name = 'cnblog'
    allowed_domains = ['www.cnblogs.com']
    start_urls = ['https:/www.cnblogs.com/']
  
    '''
    爬取原则:scrapy默认是先进先出
        -深度优先:详情页先爬   队列,先进去先出来
        -广度优先:每一页先爬   栈  ,后进先出
    
    '''
   def parse(self, response):
        # print(response.text)
        div_list=response.css('article.post-item')
        for div in div_list:
            item=CnblogsItem()
            title=div.xpath('.//div[1]/a/text()').extract_first()
            item['title']=title
            url=div.xpath('.//div[1]/a/@href').extract_first()
            item['url'] = url
            desc=div.xpath('.//div[1]/p/text()').extract_first().strip()
            item['desc'] = desc
            # 要继续爬取详情
            # callback如果不写,默认回调到parse方法
            # 如果写了,响应回来的对象就会调到自己写的解析方法中
            yield Request(url,callback=self.parser_detail,meta={'item':item})
    
        # 解析出下一页的地址
        next='https://www.cnblogs.com'+response.css('#paging_block>div a:last-child::attr(href)').extract_first()
        # print(next)
        yield Request(next)
    
    def parser_detail(self,response):
    
        content=response.css('#cnblogs_post_body').extract_first()
        print(str(content))
        # item哪里来
        item=response.meta.get('item')
        item['content']=content
        yield item

piplines文件

import pymysql

class CnblogsMysqlPipeline(object):
    def open_spider(self, spider):
        # 爬虫对象
        print('-------', spider.name)
        self.conn = pymysql.connect(
            host='127.0.0.1', user='root', password="123", database='cnblogs', port=3306)

    def process_item(self, item, spider):
        cursor = self.conn.cursor()
        sql = 'insert into article (title,url,content,`desc`) values (%s,%s,%s,%s)'
        cursor.execute(sql, [item['title'], item['url'],
                       item['content'], item['desc']])
        self.conn.commit()
        return item

    def close_spider(self, spider):
        self.conn.close()

scrapy的请求传参

# 把要传递的数据放到meta中
yield Request(urlmeta={'item':item})
# 在response对象中取出来
item=response.meta.get('item')

提升scrapy爬取数据的效率

- 在配置文件中进行相关的配置即可:(默认还有一套setting)
#1 增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’
# 3 禁止cookie:
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
# 4禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
# 5 减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

scrapy的中间件(下载中间件)

注意:
1 scrapy的在中间都写在middlewares.py
2 自带了爬虫中间件、下载中间件
3 要生效,一定要配置,配置文件

# 下载中间件
-process_request:返回不同的对象,后续处理不同(加代理...)
  		# 1 更换请求头
        # print(type(request.headers))
        # print(request.headers)
        #
        # 查看header的继承路径from scrapy.http.headers import Headers发现是字典
        # request.headers['User-Agent']=''

        # 2 加cookie ---cookie池
        # 假设搭建好cookie 池了,
        # print('00000--', request.cookies)
        # request.cookies = {'username':'asdfasdf'}

        # 3 加代理
        # print(request.meta) 
        # request.meta['download_timeout'] = 20
        # request.meta["proxy"] = 'http://27.188.62.3:8060'

-process_response:返回不同的对象,后续处理不同
- process_exception
def process_exception(self, request, exception, spider):
        print('xxxx')
        # 不允许直接改url
        # request.url='https://www.baidu.com'
        from scrapy import Request
        request=Request(url='https://www.baidu.com',callback=spider.parser)
        return request

selenium在scrapy中的使用流程

# 当前爬虫用的selenium是同一个

# 1 在爬虫中初始化webdriver对象
    from selenium import webdriver
    class CnblogSpider(scrapy.Spider):
        name = 'cnblog'
        ...
bro=webdriver.Chrome(executable_path='../chromedriver.exe')
# 2 在中间件中使用(process_request)
spider.bro.get('https://dig.chouti.com/')
response=HtmlResponse(url='https://dig.chouti.com/',body=spider.bro.page_source.encode('utf-8'),request=request)
    return response
	
# 3 在爬虫中关闭
    def close(self, reason):
        print("我结束了")
        self.bro.close()

去重规则

去重规则应该多个爬虫共享的,但凡一个爬虫爬取了,其他都不要爬了,实现方式如下

#方法一:
1、新增类属性
visited=set() #类属性

2、回调函数parse方法内:
def parse(self, response):
    if response.url in self.visited:
        return None
    .......
    self.visited.add(response.url) 

#方法一改进:针对url可能过长,所以我们存放url的hash值
def parse(self, response):
        url=md5(response.request.url)
    if url in self.visited:
        return None
    .......

self.visited.add(url) 

#方法二:Scrapy自带去重功能
配置文件:
DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' #默认的去重规则帮我们去重,去重规则在内存中
DUPEFILTER_DEBUG = False
JOBDIR = "保存范文记录的日志路径,如:/root/"  # 最终路径为 /root/requests.seen,去重规则放文件中

scrapy自带去重规则默认为RFPDupeFilter,只需要我们指定
Request(...,dont_filter=False) ,如果dont_filter=True则告诉Scrapy这个URL不参与去重。

#方法三:
我们也可以仿照RFPDupeFilter自定义去重规则,

from scrapy.dupefilter import RFPDupeFilter,看源码,仿照BaseDupeFilter

#步骤一:在项目目录下自定义去重文件dup.py
class UrlFilter(object):
    def __init__(self):
        self.visited = set() #或者放到数据库

    @classmethod
    def from_settings(cls, settings):
        return cls()


def request_seen(self, request):
    if request.url in self.visited:
        return True
    self.visited.add(request.url)

def open(self):  # can return deferred
    pass

def close(self, reason):  # can return a deferred
    pass

def log(self, request, spider):  # log that a request has been filtered
    pass


#步骤二:配置文件settings.py:
DUPEFILTER_CLASS = '项目名.dup.UrlFilter'

# 源码分析:
from scrapy.core.scheduler import Scheduler
见Scheduler下的enqueue_request方法:self.df.request_seen(request)

分布式爬虫(scrapy-redis)

# 1 pip3 install scrapy-redis
# 2 原来继承Spider,现在继承RedisSpider
# 3 不能写start_urls = ['https:/www.cnblogs.com/']
# 4 需要写redis_key = 'myspider:start_urls'
# 5 setting中配置:

# redis的连接
REDIS_HOST = 'localhost'                            # 主机名
REDIS_PORT = 6379                                   # 端口

# 使用scrapy-redis的去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis的Scheduler
# 分布式爬虫的配置
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 持久化的可以配置,也可以不配置
ITEM_PIPELINES = {
   'scrapy_redis.pipelines.RedisPipeline': 299
}


# 现在要让爬虫运行起来,需要去redis中以myspider:start_urls为key,插入一个起始地址lpush myspider:start_urls https://www.cnblogs.com/

破解知乎登陆(js逆向和解密)

详细分析文章:知乎破解加密算法模拟登陆 访问密码:xiaoyuanqujing@666

client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&
grant_type=password&
timestamp=1596702006088&
source=com.zhihu.web&
signature=eac4a6c461f9edf86ef33ef950c7b6aa426dbb39&
username=%2B86liuqingzheng&
password=1111111&
captcha=&
lang=en&
utm_source=&
ref_source=other_https%3A%2F%2Fwww.zhihu.com%2Fsignin%3Fnext%3D%252F"


# 破解知乎登陆

import requests    #请求解析库

import base64							  #base64解密加密库
from PIL import Image	  			      #图片处理库
import hmac								  #加密库
from hashlib import sha1				  #加密库
import time
from urllib.parse import urlencode		  #url编码库
import execjs							  #python调用node.js
from http import cookiejar as cookielib
class Spider():
    def __init__(self):
        self.session = requests.session()
        self.session.cookies = cookielib.LWPCookieJar()    #使cookie可以调用save和load方法
        self.login_page_url = 'https://www.zhihu.com/signin?next=%2F'
        self.login_api = 'https://www.zhihu.com/api/v3/oauth/sign_in'
        self.captcha_api = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
        self.headers = {
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER',
        }

        self.captcha =''         #存验证码
        self.signature = ''	   #存签名

    # 首次请求获取cookie
    def get_base_cookie(self):
        self.session.get(url=self.login_page_url, headers=self.headers)

    def deal_captcha(self):
        r = self.session.get(url=self.captcha_api, headers=self.headers)
        r = r.json()
        if r.get('show_captcha'):
            while True:
                r = self.session.put(url=self.captcha_api, headers=self.headers)
                img_base64 = r.json().get('img_base64')
                with open('captcha.png', 'wb') as f:
                    f.write(base64.b64decode(img_base64))
                captcha_img = Image.open('captcha.png')
                captcha_img.show()
                self.captcha = input('输入验证码:')
                r = self.session.post(url=self.captcha_api, data={'input_text': self.captcha},
                                      headers=self.headers)
                if r.json().get('success'):
                    break

    def get_signature(self):
        # 生成加密签名
        a = hmac.new(b'd1b964811afb40118a12068ff74a12f4', digestmod=sha1)
        a.update(b'password')
        a.update(b'c3cef7c66a1843f8b3a9e6a1e3160e20')
        a.update(b'com.zhihu.web')
        a.update(str(int(time.time() * 1000)).encode('utf-8'))
        self.signature = a.hexdigest()

    def post_login_data(self):
        data = {
            'client_id': 'c3cef7c66a1843f8b3a9e6a1e3160e20',
            'grant_type': 'password',
            'timestamp': str(int(time.time() * 1000)),
            'source': 'com.zhihu.web',
            'signature': self.signature,
            'username': '+8618953675221',
            'password': '',
            'captcha': self.captcha,
            'lang': 'en',
            'utm_source': '',
            'ref_source': 'other_https://www.zhihu.com/signin?next=%2F',
        }

        headers = {
            'x-zse-83': '3_2.0',
            'content-type': 'application/x-www-form-urlencoded',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER',
        }

        data = urlencode(data)
        with open('zhih.js', 'rt', encoding='utf-8') as f:
            js = execjs.compile(f.read(), cwd='node_modules')
        data = js.call('b', data)

        r = self.session.post(url=self.login_api, headers=headers, data=data)
        print(r.text)
        if r.status_code == 201:
            self.session.cookies.save('mycookie')
            print('登录成功')
        else:
            print('登录失败')

    def login(self):
        self.get_base_cookie()
        self.deal_captcha()
        self.get_signature()
        self.post_login_data()
if __name__ == '__main__':
    zhihu_spider = Spider()
    zhihu_spider.login()

爬虫的反扒措施

1 user-agent
2 referer
3 cookie(cookie池,先访问一次)
4 频率限制(代理池,延迟)
5 js加密(扣出来,exjs模块指向)
6 css加密
7 验证码(打码平台),半手动
8 图片懒加载

拓展

热更新:不停机更新

就是说
你的卡车开到了150KM/H
然后,有个轮胎,爆了
然后,司机说,你就直接换吧,我不停车。你小心点换

嗯。就这个意思

3分钟看懂Python后端必须知道的Django的信号机制!

红薯小说网爬取(6层反扒,涉及js加密,js动态渲染,css反扒等等)

posted @   喝茶看狗叫  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示