美文网首页
代理池的搭建/resis请求队列/mysql存储微信文章

代理池的搭建/resis请求队列/mysql存储微信文章

作者: strive鱼 | 来源:发表于2019-03-21 17:53 被阅读0次

    本章主要是动手实践操作爬取微信文章并存储到数据库,学习到的东西如下:

    • 1 代理池的搭建
    • 2 请求队列的构建和redis存储
    • 3 mysql的数据存储

    1.关于代理池的搭建

    直接从该链接下载安装相应组件运行即可
    https://github.com/python3webspider/proxypool
    随后进入到爬虫代码部分,文件的位置如下,运行前,确保代理池开启正常获取相关爬取链接的代理

    文件的位置

    2.相关文件及代码注解

    (1) config.py
    其中,REDIS_KEY存储请求连接的数据库名称,存储所有的请求链接构成数据队列;PROXY_POOL_URL获取代理的接口;MYSQL_DATABASE 存储所有相关爬取信息数据的数据库名

    ######该文件的作用主要是去声明几个变量
    
    REDIS_HOST = 'localhost'
    
    REDIS_PORT = 6379
    
    REDIS_PASSWORD = None #redis 没有设定密码
    
    REDIS_KEY = 'weixin'
    
    PROXY_POOL_URL = 'http://127.0.0.1:5555/random'##web端的接口,用于获取代理
    
    MYSQL_HOST = 'localhost'
    
    MYSQL_PORT = 3306
    
    MYSQL_USER = 'root'
    
    MYSQL_PASSWORD = '123456'
    
    MYSQL_DATABASE = 'weixin'#  Mysql 里面存储数据仓库名称
    

    (2)request.py 用于设定请求链接的格式,即需要哪些参数

    mport config
    from config import *
    from requests import Request
    
    
    
    
    ####这个的作用是,本项目是将请求的链接作为队列存储,那么不可能每一个链接的参数都设置一遍,这就需要调用requests里的Request
    ##这样,就可以为每个连接设定基本参数,下面参数包含了请求方式,请求的连接,解析连接用的回调函数(calllback),请求头,是否需要代理
    ##最长的容出时间,以及请求失败的次数,失败次数太多就不再请求
    class WeixinRequest(Request):
        def __init__(self, url, callback, method='GET', headers=None, need_proxy=False, fail_time=0, timeout=TIMEOUT):
            Request.__init__(self, method, url, headers)
            self.callback = callback
            self.need_proxy = need_proxy
            self.fail_time = fail_time
            self.timeout = timeout
    

    (3) db.py 当请求链接满足request.py 里面的设定,则将其加入redis数据库

    ########该文件的作用是是西安请求队列
    
    
    
    from redis import StrictRedis#用于连接参数
    import config
    from config import *#s呼出存储的参数
    from pickle import dumps,loads
    """
    这两个的作用在崔庆才的书中有相关的注解,我们在request 里面存储了连链接的相关参数,生成了Request对象
    但是在Redis 中不能直接存取Request对象,只能存取字符串,因此存取的时候需要序列化,取出的时候需要将其反序列化
    """
    import request
    from request import WeixinRequest
    
    
    class redisque(object):
        def __init__(self):
            """
            连接本地redis
            """
            self.db=StrictRedis(host=REDIS_HOST,port=REDIS_PORT,password=REDIS_PASSWORD)
    
    
        def add(self,request):
            """
    
            :param request: 请求对象
            :return: 将对象添加到redis 队列
            """
            # 如果给定的request 满足WeixinRequest类的参数设定
            if isinstance(request,WeixinRequest):
                return self.db.rpush(REDIS_KEY,dumps(request)) #指定redis_key后,利用dump将其序列化然后传入redis队列
            return False
    
    
        def pop(self):
            """
            取出下一个Request 并反序列
            :return: Request or None
            """
            if self.db.llen(REDIS_KEY):#如果redis队列的长度不为0
                return loads(self.db.lpop(REDIS_KEY))#就从该指定的key中弹出一个请求链接,lpop 是先进先出,rpop 是先进后出
            else:
                return False
    
    
        def clear(self):
            self.db.delate(REDIS_KEY)
    
    
        def empty(self):
            return self.db.llen(REDIS_KEY)==0#不删除,只是清空
    
    
    
    
    if __name__=='__main__':
        db=redisque()#首先实例化
        start_url='http://www.baidu.com'#传入一个起始的链接
        weixin_request=WeixinRequest(url=start_url,callback='hello',method='GET',need_proxy=True)#设定基础参数,回调函数开始就先写为hello,开头嘛,打个招呼
        db.add(weixin_request)
        request=db.pop()#取出一个链接
        print(request)
        print(request.callback,request.need_proxy)#顺便将该链接的回调值和请求代理打印出来
    

    (4) mysql.py 用于将数据存入到数据库

    import pymysql
    import config
    from config import *
    
    
    
    
    
    
    class mysql(object):
        def __init__(self,host=MYSQL_HOST,username=MYSQL_USER,password=MYSQL_PASSWORD,port=MYSQL_PORT,database=MYSQL_DATABASE):#指定一些初始化变量,用户名,密码,端口,数据库名称
            try:
                self.db = pymysql.connect(host, username, password, database, charset='utf8', port=port)  # 表字段设定为utf-8
                self.cursor=self.db.cursor()##建立数据库的链接,设定游标
            except pymysql.MySQLError  as e:#报错
                print (e.args)
    
    
        def insert(self,table,data):
            """
            插入数据
            :param table:
            :param data:
            :return:
            """
    
            keys=",".join(data.keys())#data是字典的类型,这一步将所有的keys取出并且分割
            values=",".join(['%s']*len(data))#根据数据的长度来增加占位符的个数,返回的结果是[%s,%s,%s.........]
            sql_query='insert into %s (%s) valiues (%s)'%(table,keys,values)#查询语句
    
    
            try:
                self.cursor.excute(sql_query,tuple(data.values()))#游标移动插入相关的键值,以元组的形式掺入
                self.db.commit()
            except pymysql.MySQLError as e:
                print (e.args)
                self.db.rollback()
    

    (5) weixinpaper.py 爬取的主程序

    ###爬虫代码的主要程序
    import config
    import db
    import mysql
    import request
    import requests
    import urllib
    import pyquery
    from pyquery import PyQuery as pq
    from db import *
    from mysql import *
    from request import *
    from config import *
    from requests import Session#会话对象,当请求多个链接的时候,可以设置一次cookies后,运用到多个链接
    from urllib.parse import urlencode
    from requests import ReadTimeout,ConnectionError
    
    
    
    
    
    class spider(object):
        base_url='http://weixin.sogou.com/weixin'
        keyword='python'
        headers = {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2,mt;q=0.2',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Cookie': 'IPLOC=CN1100; SUID=6FEDCF3C541C940A000000005968CF55; SUV=1500041046435211; ABTEST=0|1500041048|v1; SNUID=CEA85AE02A2F7E6EAFF9C1FE2ABEBE6F; weixinIndexVisited=1; JSESSIONID=aaar_m7LEIW-jg_gikPZv; ld=Wkllllllll2BzGMVlllllVOo8cUlllll5G@HbZllll9lllllRklll5@@@@@@@@@@; LSTMV=212%2C350; LCLKINT=4650; ppinf=5|1500042908|1501252508|dHJ1c3Q6MToxfGNsaWVudGlkOjQ6MjAxN3x1bmlxbmFtZTo1NDolRTUlQjQlOTQlRTUlQkElODYlRTYlODklOEQlRTQlQjglQTglRTklOUQlOTklRTglQTclODV8Y3J0OjEwOjE1MDAwNDI5MDh8cmVmbmljazo1NDolRTUlQjQlOTQlRTUlQkElODYlRTYlODklOEQlRTQlQjglQTglRTklOUQlOTklRTglQTclODV8dXNlcmlkOjQ0Om85dDJsdUJfZWVYOGRqSjRKN0xhNlBta0RJODRAd2VpeGluLnNvaHUuY29tfA; pprdig=ppyIobo4mP_ZElYXXmRTeo2q9iFgeoQ87PshihQfB2nvgsCz4FdOf-kirUuntLHKTQbgRuXdwQWT6qW-CY_ax5VDgDEdeZR7I2eIDprve43ou5ZvR0tDBlqrPNJvC0yGhQ2dZI3RqOQ3y1VialHsFnmTiHTv7TWxjliTSZJI_Bc; sgid=27-27790591-AVlo1pzPiad6EVQdGDbmwnvM; PHPSESSID=mkp3erf0uqe9ugjg8os7v1e957; SUIR=CEA85AE02A2F7E6EAFF9C1FE2ABEBE6F; sct=11; ppmdig=1500046378000000b7527c423df68abb627d67a0666fdcee; successCount=1|Fri, 14 Jul 2017 15:38:07 GMT',
            'Host': 'weixin.sogou.com',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
        }
        session=Session()#实例化会话
        queue=redisque()#实例化代理池队列
        mysql=mysql()#实例化存储对象
        
        """
        def __init__(self,base_url,keyword,headers,session,queue,mysql):
            self.base_url=base_url
            self.keyword=keyword
            self.headers=headers
            self.session=session
            self.queue=queue
            self.mysql=mysql
        """
            
            
        def get_proxy(self):#首先拿出一个代理
            """
    
            :return: 返回一个代理
            """
            try:
                response=requests.get(PROXY_POOL_URL)#请求config 中存储的一个web前端获取代理的接口,使用时必须打开代理池代码
                if response.status_code==200:
                    print ('success to get proxy',response.text)
                    return response.text
                return None
            except requests.ConnectionError as e:
                return e.args
    
    
        def  start(self):
            """
    
            :return: 构建请求链接,并将其加入到redis 队列里面
            """
    
            self.session.headers.update(self.headers)#全局的更新headers
            start_url=sef.base_url+'?'+urlencode({'query':self.keyword,'type':2})#https://weixin.sogou.com/weixin?type=2&query=python,请求连接的格式
            weixin_request=WeixinRequest(url=start_url,callback=self.parse_index,need_proxy=True)#说明两点,1已经设定了所有链接的全局headers,因此不需要再设定headers,另外,callback 指定解析的函数
            self.queue.add(weixin_request)#加入到redis 队列
    
    
    
        def parse_index(self,response):
            """
    
            :param response: 响应
            :return: 新的响应
            """
            doc=pq(response.text)#pyquery 解析界面
            items=doc('.news-box .news-list li .txt-box h3 a').items()#进入到主页,主页里面有很多文章,这一步是拿出所有文章所在链接的列表
            for item in items:
                url=item.attr('href')#获得每一篇文章的链接
                weixin_request=WeixinRequest(url=url,callback=self.parse_detail)#不必要添加代理,另外回调函数指给详情解析函数
                yield weixin_request#实际上最后返回的是解析详情页后的数据构成的字典
    
            next=doc('#sogou_next').attr('href')#锁定下一页取出对应的链接,值为请求参数
            if next:
                next_url=self.base_url+str(next)
                weixin_request=WeixinRequest(url=next_url,need_proxy=True,callback=self.parse_index)#回调自己
                yield weixin_request
    
        def parse_detail(self, response):
            """
    
            :param response: 响应
            :return: 返回微信文章详情页的信息
            """
    
            doc=pq(response.text)
            data={'title':doc('.rich_media_title').text(),'content':doc('.rich_media_content ').text(),'date':doc('.js_profile_qrcode #publish_time').text(),'nickname':doc('#meta_content .rich_media_meta rich_media_meta_text').text()}#以字典的形式存储文章内容的标题,内容,数据,微信昵称,微信号
            yield data#生成器
    
    
    
            """
            上述完成了主页的解析
            详情页的解析
            还差requests.get(weixin_request)环节
            因此需要一个请求函数,下面的方法是通过会话对象发送请求
            """
    
        def request(self,weixin_request):
            """
    
            :param weixin_request: 请求
            :return: 响应
            """
            try:
                if weixin_request.need_proxy:#参数类似self 的属性
                    proxy=self.get_proxy()#根据方法从接口获得一个代理
                    if proxy:#成功获取
                        proxise={'http://'+proxy,'https://'+proxy}
                        return self.session.send(weixin_request.prepare(),timeout=weixin_request.timeout, allow_redirects=False, proxies=proxies)#利用会话
                return self.session.send(weixin_request.prepare(), timeout=weixin_request.timeout, allow_redirects=False)
            except (ConnectionError, ReadTimeout) as e:
                print (e.args)
    
    
    
    
        def error(self,weixin_request):
            """
    
            :param weixin_request: 处理错误
            :return:
            """
            weixin_request.fail_time=weixin_request.fail_time+1#迭代
            print('Request Failed', weixin_request.fail_time, 'Times', weixin_request.url)
            if weixin_request.fail_time:#config里面设定的最大失败次数
                self.queue.add(weixin_request)#重新加入到调度队列中
    
    
    
        def schedule(self):#最为关键的调度队列
            """
            调度请求
            :return:
            """
            while not self.queue.empty():#当不为空的时候
                weixin_request=self.queue.pop()#取出一个链接
                callback=weixin_request.callback#看下面的run 代码,首先调用的是start函数,因此回调函数就是parse_index
                print ('schedule',weixin_request.url)
                response=self.request(weixin_request)#请求相应
                if response and response.status_code in VALID_STATUSES:
                    results=list(callback(response))#利用回调函数指定解析函数
                    if results:
                        for result in results:
                            print('New Result', type(result))
                            if isinstance(result,WeixinRequest):
                                self.queue.add(result)
                            if isinstance(result,dict):#字典类型说明是解析内容
                                self.mysql.insert('articles',result)
                    else:
                        self.error(weixin_request)
                else:
                    self.error(weixin_request)
    
    
    
    
        def run(self):
            self.start()
            self.schedule()
    
    
    
    
    
    if __name__=='_-main__':
        spider=spider()
        spider.run()
    

    通过此次实践学会了代理池的搭建,可以用于反爬,另外还学会了简单redis和mysql数据库的存储使用

    相关文章

      网友评论

          本文标题:代理池的搭建/resis请求队列/mysql存储微信文章

          本文链接:https://www.haomeiwen.com/subject/prmcvqtx.html