很多时候我们在对网站进行数据抓取的时候,可以抓取一部分页面或者接口,这部分可能没有设置登录限制。但是如果要抓取大规模数据的时候,没有登录进行爬取会出现一些弊端。对于一些设置登录限制的页面,无法爬取对于一些没有设置登录的页面或者接口,一旦IP访问频繁,会触发网站的反爬虫,相比较代理池通过改变IP地址来避免被网站封禁,但是现在的有的网站已经不封IP地址,开始封账号的反爬措施,如果做大规模爬虫怎么办呢,一个账号有可能被封,如果像代理池一样提供不同IP,我有多个账号轮流爬取是不是可以避免被封。所有就需要维护多个账号,这个时候就要用到cookies池了,通过获取每个账号模拟登录后的cookies信息,保存到数据库,定时检测cookies有效性。

1.相关工具

安装Redis数据库,redis-py库,reuqests,selenium,Flask 库,还有Google Chrome浏览器 安装好ChromeDriver,购买要爬取的网站账号,比如我购买的微博小号(卖号网站随机百度,有些网站不固定容易404,最好买免验证码登录的) 这里我们搭建微博的Cookies池

2.cookies实现

 需要下图几大模块

 存储模块负责存储每个账号的用户名,密码已经每个账号对应的Cookies信息,同时提供方法对数据的存储操作

 生成模块 负责获取登录之后的Cookies,这个模块要从数据库中取账号密码,再模拟登录目标页面,如果登陆成功,就获取Cookies保存到数据库

 检测模块负责定时检测数据库中的Cookies是否有效,使用Cookies请求链接,如果登录状态成功,则是有效的,否则失效并删除,接下来等待生产模块重新登录生成Cookies

 接口模块是通过Api提供对外服务的接口,Cookies越多越好,被检测到的概率越小,越不容易被封

接下来实现存储模块:存储部分有两部分:1.账号和密码 2.账号和Cookies 这两部分是一对一对应的,所以可以使用Redis的hash ,hash存储结构是key-value 也就是键值对的形式,和我们所需的是符合的,所以就有两组映射,账号密码 ,账号Cookies, key是账号

  1. import random
  2. import redis

# Redis数据库地址
REDIS_HOST = 'localhost'

# Redis端口
REDIS_PORT = 6379

# Redis密码,如无填None
REDIS_PASSWORD = None

  1. class RedisClient(object):
  2. def __init__(self, type, website, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD):
  3. """
  4. 初始化Redis连接
  5. :param host: 地址
  6. :param port: 端口
  7. :param password: 密码
  8. """
  9. self.db = redis.StrictRedis(host=host, port=port, password=password, decode_responses=True)
  10. self.type = type
  11. self.website = website
  12.  
  13. def name(self):
  14. """
  15. 获取Hash的名称
  16. :return: Hash名称
  17. """
  18. return "{type}:{website}".format(type=self.type, website=self.website)
  19.  
  20. def set(self, username, value):
  21. """
  22. 设置键值对
  23. :param username: 用户名
  24. :param value: 密码或Cookies
  25. :return:
  26. """
  27. return self.db.hset(self.name(), username, value)
  28.  
  29. def get(self, username):
  30. """
  31. 根据键名获取键值
  32. :param username: 用户名
  33. :return:
  34. """
  35. return self.db.hget(self.name(), username)
  36.  
  37. def delete(self, username):
  38. """
  39. 根据键名删除键值对
  40. :param username: 用户名
  41. :return: 删除结果
  42. """
  43. return self.db.hdel(self.name(), username)
  44.  
  45. def count(self):
  46. """
  47. 获取数目
  48. :return: 数目
  49. """
  50. return self.db.hlen(self.name())
  51.  
  52. def random(self):
  53. """
  54. 随机得到键值,用于随机Cookies获取
  55. :return: 随机Cookies
  56. """
  57. return random.choice(self.db.hvals(self.name()))
  58.  
  59. def usernames(self):
  60. """
  61. 获取所有账户信息
  62. :return: 所有用户名
  63. """
  64. return self.db.hkeys(self.name())
  65.  
  66. def all(self):
  67. """
  68. 获取所有键值对
  69. :return: 用户名和密码或Cookies的映射表
  70. """
  71. return self.db.hgetall(self.name())
  72.  
  73. if __name__ == '__main__':
  74. conn = RedisClient('accounts', 'weibo')
  75. result = conn.set('wert', 'ssdsdsf')
  76. print(result)

我们可以看见name()方法,返回值就是存储key-value 的hash名称 比如accounts:weibo 存储的就是账号和密码,通过这种方式可以把账号密码添加进数据库

生成模块的实现

要获取微博登录信息的Cookies,肯定要登录微博,但是微博这网站的登录接口需要填写验证码 或者手机号验证比较复杂,比较好的是微博登录站点有三个 1.https://weibo.cn  2.https://m.weibo.com

3.https://weibo.com  我们选择第二个站点比较合适,类似于手机客户端的界面,而且登录的时候不需要验证码(前提购买免验证码账号才可以)登录界面是这样

  1. import json
  2. from selenium import webdriver
  3. from selenium.webdriver import DesiredCapabilities
  4.  
  5. from cookiespool.db import RedisClient
  6. from login.weibo.cookies import WeiboCookies

# 产生器使用的浏览器
BROWSER_TYPE = 'PhantomJS'

  1. class CookiesGenerator(object):
  2. def __init__(self, website='default'):
  3. """
  4. 父类, 初始化一些对象
  5. :param website: 名称
  6. :param browser: 浏览器, 若不使用浏览器则可设置为 None
  7. """
  8. self.website = website
  9. self.cookies_db = RedisClient('cookies', self.website)
  10. self.accounts_db = RedisClient('accounts', self.website)
  11. self.init_browser()
  12.  
  13. def __del__(self):
  14. self.close()
  15.  
  16. def init_browser(self):
  17. """
  18. 通过browser参数初始化全局浏览器供模拟登录使用
  19. :return:
  20. """
  21. if BROWSER_TYPE == 'PhantomJS':
  22. caps = DesiredCapabilities.PHANTOMJS
  23. caps[
  24. "phantomjs.page.settings.userAgent"] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
  25. self.browser = webdriver.PhantomJS(desired_capabilities=caps)
  26. self.browser.set_window_size(1400, 500)
  27. elif BROWSER_TYPE == 'Chrome':
  28. self.browser = webdriver.Chrome()
  29.  
  30. def new_cookies(self, username, password):
  31. """
  32. 新生成Cookies,子类需要重写
  33. :param username: 用户名
  34. :param password: 密码
  35. :return:
  36. """
  37. raise NotImplementedError
  38.  
  39. def process_cookies(self, cookies):
  40. """
  41. 处理Cookies
  42. :param cookies:
  43. :return:
  44. """
  45. dict = {}
  46. for cookie in cookies:
  47. dict[cookie['name']] = cookie['value']
  48. return dict
  49.  
  50. def run(self):
  51. """
  52. 运行, 得到所有账户, 然后顺次模拟登录
  53. :return:
  54. """
  55. accounts_usernames = self.accounts_db.usernames()
  56. cookies_usernames = self.cookies_db.usernames()
  57.  
  58. for username in accounts_usernames:
  59. if not username in cookies_usernames:
  60. password = self.accounts_db.get(username)
  61. print('正在生成Cookies', '账号', username, '密码', password)
  62. result = self.new_cookies(username, password)
  63. # 成功获取
  64. if result.get('status') == 1:
  65. cookies = self.process_cookies(result.get('content'))
  66. print('成功获取到Cookies', cookies)
  67. if self.cookies_db.set(username, json.dumps(cookies)):
  68. print('成功保存Cookies')
  69. # 密码错误,移除账号
  70. elif result.get('status') == 2:
  71. print(result.get('content'))
  72. if self.accounts_db.delete(username):
  73. print('成功删除账号')
  74. else:
  75. print(result.get('content'))
  76. else:
  77. print('所有账号都已经成功获取Cookies')
  78.  
  79. def close(self):
  80. """
  81. 关闭
  82. :return:
  83. """
  84. try:
  85. print('Closing Browser')
  86. self.browser.close()
  87. del self.browser
  88. except TypeError:
  89. print('Browser not opened')
  90.  
  91. class WeiboCookiesGenerator(CookiesGenerator):
  92. def __init__(self, website='weibo'):
  93. """
  94. 初始化操作
  95. :param website: 站点名称
  96. :param browser: 使用的浏览器
  97. """
  98. CookiesGenerator.__init__(self, website)
  99. self.website = website
  100.  
  101. def new_cookies(self, username, password):
  102. """
  103. 生成Cookies
  104. :param username: 用户名
  105. :param password: 密码
  106. :return: 用户名和Cookies
  107. """
  108. return WeiboCookies(username, password, self.browser).main()

这部分是来判断是否登录成功

  1. import time
  2. from io import BytesIO
  3. from PIL import Image
  4. from selenium.common.exceptions import TimeoutException
  5. #from selenium.webdriver import ActionChains
  6. from selenium.webdriver.common.by import By
  7. from selenium.webdriver.support.ui import WebDriverWait
  8. from selenium.webdriver.support import expected_conditions as EC
  9. from os import listdir
  10. from os.path import abspath, dirname
  11.  
  12. TEMPLATES_FOLDER = dirname(abspath(__file__)) + '/templates/'
  13.  
  14. class WeiboCookies():
  15. def __init__(self, username, password, browser):
  16. self.url = 'https://passport.weibo.cn/signin/login?entry=mweibo&r=https://m.weibo.cn/'
  17. self.browser = browser
  18. self.wait = WebDriverWait(self.browser, 20)
  19. self.username = username
  20. self.password = password
  21.  
  22. def open(self):
  23. """
  24. 打开网页输入用户名密码并点击
  25. :return: None
  26. """
  27. self.browser.delete_all_cookies()
  28. self.browser.get(self.url)
  29. username = self.wait.until(EC.presence_of_element_located((By.ID, 'loginName')))
  30. password = self.wait.until(EC.presence_of_element_located((By.ID, 'loginPassword')))
  31. submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'loginAction')))
  32. username.send_keys(self.username)
  33. password.send_keys(self.password)
  34. time.sleep(1)
  35. submit.click()
  36.  
  37. def password_error(self):
  38. """
  39. 判断是否密码错误
  40. :return:
  41. """
  42. try:
  43. return WebDriverWait(self.browser, 5).until(
  44. EC.text_to_be_present_in_element((By.ID, 'errorMsg'), '用户名或密码错误'))
  45. except TimeoutException:
  46. return False
  47.  
  48. def login_successfully(self):
  49. """
  50. 判断是否登录成功
  51. :return:
  52. """
  53. try:
  54. return bool(
  55. WebDriverWait(self.browser, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'lite-iconf-profile'))))
  56. except TimeoutException:
  57. return False
  58.  
  59. def get_cookies(self):
  60. """
  61. 获取Cookies
  62. :return:
  63. """
  64. return self.browser.get_cookies()
  65.  
  66. def main(self):
  67. """
  68. 破解入口
  69. :return:
  70. """
  71. self.open()
  72. if self.password_error():
  73. return {
  74. 'status': 2,
  75. 'content': '用户名或密码错误'
  76. }
  77. # 如果不需要验证码直接登录成功
  78. if self.login_successfully():
  79. cookies = self.get_cookies()
  80. return {
  81. 'status': 1,
  82. 'content': cookies
  83. }

检测模块:获取到Cookies信息后还需要对Cookies的有效性进行检测,也就是通过登录返回的Response的状态码判断是否有效

  1. import json
  2. import requests
  3. from requests.exceptions import ConnectionError
  4. from cookiespool.db import *
  5.  
  6. class ValidTester(object):
  7. def __init__(self, website='default'):
  8. self.website = website
  9. self.cookies_db = RedisClient('cookies', self.website)
  10. self.accounts_db = RedisClient('accounts', self.website)
  11.  
  12. def test(self, username, cookies):
  13. raise NotImplementedError
  14.  
  15. def run(self):
  16. cookies_groups = self.cookies_db.all()
  17. for username, cookies in cookies_groups.items():
  18. self.test(username, cookies)
  19.  
  20. class WeiboValidTester(ValidTester):
  21. def __init__(self, website='weibo'):
  22. ValidTester.__init__(self, website)
  23.  
  24. def test(self, username, cookies):
  25. print('正在测试Cookies', '用户名', username)
  26. try:
  27. cookies = json.loads(cookies)
  28. except TypeError:
  29. print('Cookies不合法', username)
  30. self.cookies_db.delete(username)
  31. print('删除Cookies', username)
  32. return
  33. try:
  34. test_url = TEST_URL_MAP[self.website]
  35. response = requests.get(test_url, cookies=cookies, timeout=5, allow_redirects=False)
  36. if response.status_code == 200:
  37. print('Cookies有效', username)
  38. else:
  39. print(response.status_code, response.headers)
  40. print('Cookies失效', username)
  41. self.cookies_db.delete(username)
  42. print('删除Cookies', username)
  43. except ConnectionError as e:
  44. print('发生异常', e.args)

接口模块驱动其他几个模块的运行

  1. import time
  2. from multiprocessing import Process
  3.  
  4. from cookiespool.api import app
  5. from cookiespool.generator import *
  6. from cookiespool.tester import *

# 产生器类,如扩展其他站点,请在此配置
GENERATOR_MAP = {
'weibo': 'WeiboCookiesGenerator'
}

# 测试类,如扩展其他站点,请在此配置
TESTER_MAP = {
'weibo': 'WeiboValidTester'
}

TEST_URL_MAP = {
'weibo': 'https://m.weibo.cn/'
}

# 产生器和验证器循环周期
CYCLE = 120

# 产生器开关,模拟登录添加Cookies
GENERATOR_PROCESS = True
# 验证器开关,循环检测数据库中Cookies是否可用,不可用删除
VALID_PROCESS = True
# API接口服务
API_PROCESS = True

  1. class Scheduler(object):
  2. @staticmethod
  3. def valid_cookie(cycle=CYCLE):
  4. while True:
  5. print('Cookies检测进程开始运行')
  6. try:
  7. for website, cls in TESTER_MAP.items():
  8. tester = eval(cls + '(website="' + website + '")')
  9. tester.run()
  10. print('Cookies检测完成')
  11. del tester
  12. time.sleep(cycle)
  13. except Exception as e:
  14. print(e.args)
  15.  
  16. @staticmethod
  17. def generate_cookie(cycle=CYCLE):
  18. while True:
  19. print('Cookies生成进程开始运行')
  20. try:
  21. for website, cls in GENERATOR_MAP.items():
  22. generator = eval(cls + '(website="' + website + '")')
  23. generator.run()
  24. print('Cookies生成完成')
  25. generator.close()
  26. time.sleep(cycle)
  27. except Exception as e:
  28. print(e.args)
  29.  
  30. @staticmethod
  31. def api():
  32. print('API接口开始运行')
  33. app.run(host=API_HOST, port=API_PORT)
  34.  
  35. def run(self):
  36. if API_PROCESS:
  37. api_process = Process(target=Scheduler.api)
  38. api_process.start()
  39.  
  40. if GENERATOR_PROCESS:
  41. generate_process = Process(target=Scheduler.generate_cookie)
  42. generate_process.start()
  43.  
  44. if VALID_PROCESS:
  45. valid_process = Process(target=Scheduler.valid_cookie)
  46. valid_process.start()

api接口

  1. import json
  2. from flask import Flask, g
  3.  
  4. from cookiespool.db import *

# API地址和端口
API_HOST = '127.0.0.1'
API_PORT = 5000

  1. __all__ = ['app']
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route('/')
  6. def index():
  7. return '<h2>Welcome to Cookie Pool System</h2>'
  8.  
  9. def get_conn():
  10. """
  11. 获取
  12. :return:
  13. """
  14. for website in GENERATOR_MAP:
  15. print(website)
  16. if not hasattr(g, website):
  17. setattr(g, website + '_cookies', eval('RedisClient' + '("cookies", "' + website + '")'))
  18. setattr(g, website + '_accounts', eval('RedisClient' + '("accounts", "' + website + '")'))
  19. return g
  20.  
  21. @app.route('/<website>/random')
  22. def random(website):
  23. """
  24. 获取随机的Cookie, 访问地址如 /weibo/random
  25. :return: 随机Cookie
  26. """
  27. g = get_conn()
  28. cookies = getattr(g, website + '_cookies').random()
  29. return cookies
  30.  
  31. @app.route('/<website>/add/<username>/<password>')
  32. def add(website, username, password):
  33. """
  34. 添加用户, 访问地址如 /weibo/add/user/password
  35. :param website: 站点
  36. :param username: 用户名
  37. :param password: 密码
  38. :return:
  39. """
  40. g = get_conn()
  41. print(username, password)
  42. getattr(g, website + '_accounts').set(username, password)
  43. return json.dumps({'status': ''})
  44.  
  45. @app.route('/<website>/count')
  46. def count(website):
  47. """
  48. 获取Cookies总数
  49. """
  50. g = get_conn()
  51. count = getattr(g, website + '_cookies').count()
  52. return json.dumps({'status': '', 'count': count})
  53.  
  54. if __name__ == '__main__':
  55. app.run(host='0.0.0.0')

运行效果如下

github:https://github.com/jzxsWZY/CookiesPool

搭建Cookie池的更多相关文章

  1. python3简单使用requests 用户代理,cookie池

    官方文档:http://docs.python-requests.org/en/master/ 参考文档:http://www.cnblogs.com/zhaof/p/6915127.html#und ...

  2. scrapy 设置cookie池

    代码已经很详细了,可以直接拿来使用了. 包含了: 从网页获取cookie 存入mongodb 定期删除cookie scrapy中间件对cookie池的取用 #!/usr/bin/python #co ...

  3. cookie池的维护

    存储形式: 存储在redis中,“spider_name:username–password":cookie 建立py文件及包含方法: initcookies() 初始化所有账号的cooki ...

  4. 进程线程协程补充、docker-compose一键部署项目、搭建代理池、requests超时设置、认证设置、异常处理、上传文件

    今日内容概要 补充:进程,线程,协程 docker-compose一键部署演示 搭建代理池 requests超时设置 requests认证设置 requests异常处理 requests上传文件 内容 ...

  5. 【Python3爬虫】教你怎么利用免费代理搭建代理池

    一.写在前面 有时候你的爬虫刚开始的时候可以正常运行,能够正常的爬取数据,但是过了一会,却出现了一个“403 Forbidden",或者是”您的IP访问频率太高“这样的提示,这就意味着你的I ...

  6. selenium模块使用详解、打码平台使用、xpath使用、使用selenium爬取京东商品信息、scrapy框架介绍与安装

    今日内容概要 selenium的使用 打码平台使用 xpath使用 爬取京东商品信息 scrapy 介绍和安装 内容详细 1.selenium模块的使用 # 之前咱们学requests,可以发送htt ...

  7. 配置个人Ip代理池

    做爬虫最害怕的两件事一个是被封账户一个是被封IP地址,IP地址可以使用代理来解决,网上有许多做IP代理的服务,他们提供大量的IP地址,不过这些地址不一定都是全部可用,因为这些IP地址可能被其他人做爬虫 ...

  8. 小白进阶之Scrapy(基于Scrapy-Redis的分布式以及cookies池)

    首先我们更新一下scrapy版本.最新版为1.3 再说一遍Windows的小伙伴儿 pip是装不上Scrapy的.推荐使用anaconda .不然还是老老实实用Linux吧. conda instal ...

  9. Python爬虫【四】Scrapy+Cookies池抓取新浪微博

    1.设置ROBOTSTXT_OBEY,由true变为false 2.设置DEFAULT_REQUEST_HEADERS,将其改为request headers 3.根据请求链接,发出第一个请求,设置一 ...

随机推荐

  1. git基础之创建ssh公钥和密钥

    用git管理过程中,不想每次输入username和password.解决的方法例如以下; 1 . 链接换成ssh协议. 2 . 生成公钥. 3. 加入ssh公钥到gitserver. 打开gitbas ...

  2. .NET中的PublicKeyToken以及强命名问题

    在.NET的GAC出现之前,以前有DLL Hell的问题.这是由于当时对于共享的DLL的处理方式.是通过採用注冊表的方式实现的.当我们安装一个程序A的时候,这个程序包括一个共享的DLL,那么这个DLL ...

  3. Mac下intellij idea环境的项目的新建、配色、快捷键的配置

    注:本文所使用的intellij idea是14.1.2的版本号. 一.项目的新建(以新建Java项目为例) 选中当中的"create new project". 接下来你会看到下 ...

  4. html鼠标事件

    jsp鼠标事件汇总 onclick 单击时触发的事件,这个比较常用 ondblclick 双击时触发的事件 onmoucedown 鼠标按下时触发的事件(个人觉得与onclick异曲同工) onmou ...

  5. RK3288获取摄像头的Sensor ID【原创】

    平台信息:内核:linux3.0.68 系统:android/android6.0平台:RK3288 作者:庄泽彬(欢迎转载,请注明作者) 邮箱:2760715357@qq.com 说明:通过I2C总 ...

  6. poj 2186(tarjan+缩点)

    Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 37083   Accepted: 15104 De ...

  7. uva10655

    Given the value of a+b and ab you will have to find the value of a n + b n Input The input file cont ...

  8. 19_传智播客iOS视频教程_类和对象

    什么是类?什么是对象? 看的见.摸的着.拿过来就可以直接使用.例如报纸就是一个对象. 学生这个东西可不可以只是指一个?就只有那一个才是学生.不是的,学生是有很多个的.它是对一类人的统称.类是统称.所以 ...

  9. IIs+php 最精简的环境配置

    一,安装IIS 1,打开控制面板->程序和功能->打开或关闭windows功能->Internet 信息服务 1>选 中web管理工具 2>选 中万维网服务 1>应 ...

  10. bzoj 1628: [Usaco2007 Demo]City skyline【贪心+单调栈】

    还以为是dp呢 首先默认答案是n 对于一个影子,如果前边的影子比它高则可以归进前面的影子,高处的一段单算: 和他一样高的话就不用单算了,ans--: 否则入栈 #include<iostream ...