概述

可关注微信订阅号 loak 查看实际效果。

代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider 包括了项目的所有代码。

此篇文章,主要记录利用Python request模块爬取LOL opgg英雄实时数据,并保存到mongodb中,爬取使用了可靠的redis维护IP代理池,这个过程已在腾讯云服务器上完成每日定时爬取,并完成个人订阅号 loak 的查询接口。

  • 以下简单写一下用到的技术,并在后面做详细的记录:

爬虫相关:爬虫模块 requests、 mongodb模块 pymongo、 html解析 BeautifulSoup、 正则 re模块、 xml解析 xmltodict

redis代理池:使用了Github IP代理池项目 jhao104/proxy_pool,在centos7完成redis安装、远程登录、密码登录等设置

mongodb:完成在centos7上,Python2的完全卸载及Python3的安装,mongodb的安装、用户管理、远程登录、可视化管理adminMongo配置

微信订阅号:完成微信订阅号 loak 的接口测试、腾讯云服务器用户消息接收、回复、保存

centos7:完成定时任务实现、记录运行日志、多命令一次执行、根据进程名查询进程pid、及杀死进程

  • 程序的目录结构
    以下是程序的目录结构,LOLGokEnv 为虚拟环境、LOL是爬虫的主要实现包、SpiderUtil为爬虫过程中的工具包、Web为微信订阅号接口包。
BaseException
+-- LOLGokEnv
+-- LOLGokSpider
 +-- Config.py
 +-- LOL
 |    +-- __init__.py
 |    +-- heroClass.py
 |    +-- mongoClient.py
 |    +-- opggSpider.py
 +-- SpiderUtil
 |    +-- __init__.py
 |    +-- hero_another_name.xml
 |    +-- wxUtil.py
 |    +-- xmlUtil.py
 +-- Web
 |    +-- __init__.py
 |    +-- wxWeb.py

爬虫实现

  • 分析opgg网站信息、元素
    当确定爬取什么的时候,肯定首先看网站的构造、内容的元素,以及使用怎么的技术实现该网站的爬取。对于opgg网站,这个是实时的LOL官方英雄信息查询网站(版本比国服稍前)。
    打开网站 http://www.op.gg/champion/statistics 不难发现,其实爬取的有价值信息,大概就是英雄的强势度(T1、T2…)、胜率、登场率,以及英雄在对应位置上的胜率、登场率、出装、加点、天赋等信息。当然,为了实现数据的价值挖掘,还是要加上数据爬取的日期、版本等便于分析的字段。
    在这里插入图片描述
    在这里插入图片描述

  • 分析该网站的加载
    分析完网站的构造、元素组成够,大概可以看一下网站是怎么加载的了,也就是分析如何实现、以及使用何种技术更好的爬取。
    打开控制台,如下图所示,其实发现网站的加载十分简单,就是get请求一个简单的url,返回就是我们需要的界面:
    在这里插入图片描述
    至于英雄的详细界面,也是get请求一个有规则的url,如:http://www.op.gg/champion/neeko/statistics/mid .分别就是op.gg/champion/英雄/statistics/位置。

  • 实现网站的抓取、解析
    通过以上两步,接下来大概就可以编写代码了。使用BeautifulSoup进行解析,这个好像也没什么好说的,可以看之前的博文 Python爬虫(六)—解析利器 BeautifulSoup ,使用到了基本就是BeautifulSoup4的select、find、find_all函数。
    下面直接上一部分代码,timeout=120主要考虑到这个网站加载比较慢,不知道是不是IP限制的问题,当然以下代码还需加上异常处理、出错次数限制等,以保证爬虫不会中断。

    # 获取英雄列表
    herohtml = requests.get(opgg_config['OPGG_MAIN_URL'], headers=headers, timeout=120, proxy=proxy).text
    # 存储所有英雄的hero对象
    list_hero = []
    soup = BeautifulSoup(herohtml, 'lxml')
    hero_items = soup.find_all(class_='champion-index__champion-item')
    hero_item_version = soup.select_one('[class~=champion-index__version]').text.split(':')[1].strip()

    for hero_item in hero_items:
        hero_en_name = hero_item['data-champion-key']
        hero_cn_name = hero_item['data-champion-name']
        hero_positions_items = hero_item.find_all(class_='champion-index__champion-item__position')
        hero_positions = []
        for hero_position in hero_positions_items:
            hero_positions.append(hero_position.text)
        hero = HeroClass(hero_en_name, hero_cn_name, hero_positions, hero_item_version)
        list_hero.append(hero)

redis维护IP代理池

以下主要是在centos7下的安装、维护。

  • redis安装
    安装我是根据博客 https://blog.csdn.net/u010623954/article/details/80037078#commentBox 这个完成的,注意就是可以打开 http://download.redis.io/releases/ 选择比较新的版本进行下载安装。基本步骤概括以下就是:
    wget http://download.redis.io/releases/redis-5.0.4.tar.gz
    tar -zxvf redis-5.0.4.tar.gz -C /usr/local/
    gcc -v 如果没有gcc则下载:yum install -y gcc
    cd /usr/local/redis-5.0.4/
    make MALLOC=libc
    cd src && make install
    cd /usr/local/redis-5.0.4/src/
    ./redis-server
    出现redis的界面,就是安装成功了。

  • redis配置

    • 后台进程方式启动:
      修改/usr/local/redis-5.0.4/redis.conf: daemonize no 将值改为yes 保存退出
      指定redis.conf文件启动: ./redis-server /usr/local/redis-4.0.6/redis.conf
    • 设置redis远程连接:
      1.因为redis默认设置允许本地连接,所以我们要将redis.conf中将bind 127.0.0.1 改为bind 0.0.0.0或者注释该行;
      2.腾讯云服务器有一个安全组,找到并添加规则允许6379端口访问
    • 设置redis连接密码:
      在redis.conf中搜索requirepass这一行,然后在合适的位置添加配置:
      requirepass yourpassword
      设置完成后执行/usr/local/bin/redis-server /usr/local/redis-4.0.6/redis.conf 更新配置
    • 设置开机自启动
      通过 ps -ef | grep redis | grep -v grep | awk ‘{print $2}’ | xargs kill -9 将redis所有进程杀死。
      1、在/etc目录下新建redis目录: mkdir /etc/redis
      2、将/usr/local/redis-5.0.4/redis.conf 文件复制一份到/etc/redis目录下,并命名为6379.conf:
      cp /usr/local/redis-5.0.4/redis.conf /etc/redis/6379.conf
      3、将redis的启动脚本复制一份放到/etc/init.d目录下:
      cp /usr/local/redis-5.0.4/utils/redis_init_script /etc/init.d/redisd
      4、设置redis开机自启动,先切换到/etc/init.d目录下,然后执行自启命令chkconfig redisd on:
      如果显示service redisd does not support chkconfig 解决方法:使用vim编辑redisd文件,在第一行加入如下两行注释,保存退出:
      #chkconfig: 2345 90 10
      #description: Redis is a persistent key-value database
      注释的意思是,redis服务必须在运行级2,3,4,5下被启动或关闭,启动的优先级是90,关闭的优先级是10。
      再次执行开机自启命令chkconfig redisd on
      启动:service redisd start
      关闭:service redisd stop
  • redis远程连接
    我是使用RedisDesktopManager进行redis的可视化管理的,工具可以在github上直接下载安装。uglide/RedisDesktopManager https://github.com/uglide/RedisDesktopManager

Python3、Mongodb、adminMongo

  • Python3安装
    这个主要是安装博客 https://www.cnblogs.com/johnny1024/p/8441396.html 完成的。主要步骤概括为以下:
    • 安装python3.6可能使用的依赖:
      yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel
    • 到python官网找到下载路径, 用wget下载
      wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz
    • 解压tgz包
      tar -zxvf Python-3.6.4.tgz
    • 把python移到/usr/local下面
      mv Python-3.6.4 /usr/local
    • 删除旧版本的python依赖
      ll /usr/bin | grep python
      rm -rf /usr/bin/python
    • 进入python目录
      cd /usr/local/Python-3.6.4/
    • 配置
      ./configure
    • 编译 make
      make
    • 编译,安装
      make install
    • 删除旧的软链接,创建新的软链接到最新的python
      rm -rf /usr/bin/python
      ln -s /usr/local/bin/python3.6 /usr/bin/python
      python -V
  • mongodb安装、卸载
    这个安装博文 https://www.cnblogs.com/hujiapeng/p/7008006.html 进行操作的,主要步骤是:
    • 配置yum管理包
      1、在路径/etc/yum.repos.d/下创建文件mongodb-org-3.4.repo
      cd /etc/yum.repos.d/
      touch mongodb-org-3.4.repo
      2、在文件mongodb-org-3.4.repo中写入如下内容
      [mongodb-org-3.4]
        name=MongoDB Repository
        baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
        gpgcheck=1
        enable=1
        gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
    • 安装mongodb
      yum install -y mongodb-org
      装mongodb-org包及其依赖包mongodb-org-server、mongodb-org-mongos、mongodb-org-shell、mongodb-org-tools
      数据库实例默认在/var/lib/mongo路径下,日志默认在/var/log/mongodb路径下,也可以通过修改/etc/mongod.conf文件的storage.dbPath和systemLog.path配置
    • 设置开机启动
      chkconfig mongod on
      service mongod start
      service mongod stop
    • 卸载
      service mongod stop
      yum erase $(rpm -qa | grep mongodb-org)
      rm -r /var/log/mongodb
      rm -r /var/log/mongodb
  • mongodb配置
    安装完成后,可以按照博文 https://www.jianshu.com/p/aadabfe3ee29 进行配置。
    • 远程连接配置
      修改配置文件mongodb.conf
      把 bind_ip=127.0.0.1 这一行注释掉或者是修改成 bind_ip=0.0.0.0
      /etc/init.d/mongodb restart
      远程连接
      mongo 134.567.345.23:27017/admin -uusername -p
    • 设置 admin

进入控制台 :mongo
创建管理员

     use admin
     db.createUser(
       {
         user: "myUserAdmin",
         pwd: "abc123",
         roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
       }
     )

重启 MongoDB
重启并进入控制台,强制使用用户名、密码认证。
mongod --auth
mongo
授权
use admin
db.auth(“myUserAdmin”, “abc123” )
添加数据库用户
没添加用户之前,操作数据库会报错:

   > use test
   switched to db test
   > db.foo.insert( { x: 1, y: 1 } )
   WriteCommandError({
       "ok" : 0,
       "errmsg" : "too many users are authenticated",
       "code" : 13,
       "codeName" : "Unauthorized"
   })

添加用户:

   use test
   db.createUser(
     {
       user: "myTester",
       pwd: "xyz123",
       roles: [ { role: "readWrite", db: "test" },
                { role: "read", db: "reporting" } ]
     }
   )

重新进入控制台,授权、执行插入操作:

      > use test
      switched to db test
      > db.auth("myTester", "xyz123" )
      1
      > db.foo.insert( { x: 1, y: 1 } )
      WriteResult({ "nInserted" : 1 })
  • adminMongo安装使用
    Github地址:mrvautin/adminMongo https://github.com/mrvautin/adminMongo
    安装参照博文 https://www.jianshu.com/p/6159fe96f53a
    安装adminMongo首先需要安装
    • node.js环境搭建
      • 下载地址:https://nodejs.org/en/download/
      • 安装完成后,打开cmd,输入 node -v,检查是否显示版本信息,如果显示即安装成功。
      • 打开cmd,输入npm -v校验npm工具是否安装成功,npm的作用就是对Node.js依赖的包进行管理,也可以理解为用来安装/卸载Node.js需要装的东西.
      • npm安装成功,在安装目录下新建node_cache,并使用cmd命令指定这个变量:

      npm config set cache “D:\XXX”

    • adminMongo下载安装
      1、git下将adminMongo源码从github上clone下来:
      git clone https://github.com/mrvautin/adminMongo.git
      没有git请安装,第一次clone会报错:Permission denied (publickey). fatal: Could not read from remote repository.,解决办法参照博文:https://www.cnblogs.com/wmr95/p/7852832.html
      2、进入adminMongo路径:
      cd adminMongo
      npm install
      3、启动应用:
      npm start 或者 node app
      打开浏览器,输入http://127.0.0.1:1234就可以看到adminMongo的界面了!
    • MongoDB连接字符串
      可参照博文: https://www.cnblogs.com/imeiba/p/5702298.html
      在cmd连接使用:mongo 134.567.345.23:27017/admin -uusername -p
      adminMongo连接字符串为:mongodb://username:pssword@IP:prot/databasename

微信订阅号接口测试与搭建

  • 微信公众平台的设置
    进入自己的订阅号,点击 基本设置 > 服务器配置 > 修改配置。设置如下图:
    在这里插入图片描述
    其中服务器地址(URL)两种写法:一种是写入域名/loak,一种是IP/loak,两者的区别是配置的中间件不一样:
    • 域名/loak 的Nginx的配置应为直接新增一个server,大概如下:
    server {  
            listen 80;  
            server_name wx.richule.com;
    
            # root需要写到python flask运行程序app.py的目录下
            root  /path/path/...;
    
            location / {
                # 传到服务器的对应端口,假如你的python flask搭建的服务器后台运行在4000端口
                proxy_pass http://127.0.0.1:4000;  # 转发到本地4000端口进行解析
            } 
            ......
            # 减少篇幅,这些就是随便配置就可以了
        } 
    
    • IP/loak 的Nginx的配置则是在解析端口(80或443)下增加 location,其中location 后面的也同样是python flask运行程序app.py的目录。
      服务器地址(URL)修改为:http://10.10.10.10/loak
      location增加一个:
          location /loak 
          {
                proxy_pass http://127.0.0.1:4000;
          } 
      
  • 服务器的验证代码
    因为微信服务器发送到我们的服务器,格式是xml的如下:
        <xml>
        <ToUserName><![CDATA[gh_866835093fea]]></ToUserName>
        <FromUserName><![CDATA[ogdotwSc_MmEEsJs9-ABZ1QL_4r4]]></FromUserName>
        <CreateTime>1478317060</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[你好]]></Content>
        <MsgId>6349323426230210995</MsgId>
        </xml>
    
    因此后台 Python flask代码则如下,其中:
from flask import Flask,request
import hashlib
import xmltodict
import time

app = Flask(__name__)

@app.route('/wx', methods=["GET", "POST"])
def getinput():
    if (request.method == "GET"):
    # 表示是第一次接入微信服务器的验证,验证完之后可以删掉
        signature=request.args.get('signature')
        timestamp=request.args.get('timestamp')
        nonce=request.args.get('nonce')
        token = "maluguang"
        list = [token, timestamp, nonce]
        list.sort()
        sha1 = hashlib.sha1()
        sha1.update(list[0].encode('utf-8'))
        sha1.update(list[1].encode('utf-8'))
        sha1.update(list[2].encode('utf-8'))
        hashcode = sha1.hexdigest()
        echostr = request.args.get("echostr")
        if hashcode == signature:
            return echostr
        else:
            return ""

    elif request.method == "POST": # 这里是验证完后的接受处理用户发送的消息
        # 表示微信服务器转发消息过来
        xml_str = request.data
        if not xml_str:
            return""
        # 对xml字符串进行解析
        xml_dict = xmltodict.parse(xml_str)
        xml_dict = xml_dict.get("xml")

        # 提取消息类型
        msg_type = xml_dict.get("MsgType")
        if msg_type == "text":
        # 表示发送的是文本消息
        # 构造返回值,经由微信服务器回复给用户的消息内容
            resp_dict = {
                "xml": {
                    "ToUserName": xml_dict.get("FromUserName"),
                    "FromUserName": xml_dict.get("ToUserName"),
                    "CreateTime": int(time.time()),
                    "MsgType": "text",
                    "Content": "you say:" + xml_dict.get("Content")
                }
            }

            # 将字典转换为xml字符串
            resp_xml_str = xmltodict.unparse(resp_dict)
            # 返回消息数据给微信服务器
            return resp_xml_str
        else:
            resp_dict = {
                "xml": {
                    "ToUserName": xml_dict.get("FromUserName"),
                    "FromUserName": xml_dict.get("ToUserName"),
                    "CreateTime": int(time.time()),
                    "MsgType": "text",
                    "Content": "Dear I Love you so much"
                }
            }
            resp_xml_str = xmltodict.unparse(resp_dict)
            # 返回消息数据给微信服务器
            return resp_xml_str
if __name__ == '__main__':
    app.run(port='4000')
  ```

## centos7一些shell脚本
* shell脚本
之前想编写一个shell,完成IP代理池运行进程的判断,在运行中则直接开始爬取,后来一直没成功,还请大神指教,到底应该怎么写,大概就是:
```javascript
PIDS=`ps -ef |grep main.py |grep -v grep | awk '{print $2}'`
if [ "$PIDS" != "" ]; then
  cd /home/LOLGokSpider/LOL && /home/LOLGokEnv/bin/python opggSpider.py > /home/cro_sh/spider.out 2>&1
else
  cd /home/proxy_pool/ && proxy_pool_env/bin/python Run/main.py
  PIDS=`ps -ef |grep main.py |grep -v grep | awk '{print $2}'`
  if [ "$PIDS" != "" ]; then
  cd /home/LOLGokSpider/LOL && /home/LOLGokEnv/bin/python opggSpider.py
  else
  "error" > /home/cro_sh/proxy_pool_error.out 2>&1
  fi
fi

缩进语法这些应该是没问题的,就是他一直只运行到 cd /home/proxy_pool/ && proxy_pool_env/bin/python Run/main.py ,并没有开始爬取的进程。

  • 根据进程名查询、杀死

    ps -ef |grep main.py |grep -v grep
    ps -ef | grep 进程名 | grep -v grep | awk ‘{print $2}’ | xargs kill -9

  • 定时任务
    在这里插入图片描述

loak订阅号效果

输入:排行 上
可以查询上单的强势英雄、胜率、登场率等
输入:寒冰
查询寒冰在各位置的占比、胜率、登场率
输入:寒冰 ad
查询寒冰在adc位置上的加点、出装、天赋等
关于英雄的名称,可以输入别名,如下的瘟疫之源可以改成老鼠。
在这里插入图片描述

个人博客:Loak 正 - 关注人工智能及互联网的个人博客
文章地址:爬虫实战(一)—利用requests、mongo、redis代理池爬取英雄联盟opgg实时英雄数据