存档

‘开发’ 分类的存档

Splash:javascript渲染服务简单介绍

2020年8月5日 评论已被关闭

我们在使用scrapy爬取web页面时,有时会碰到无法获取某些动态页面中的元素的情况。

这时候一般有3种方法:

  1. 抓包,分析页面中动态元素的真实请求,重新发送请求以获取元素数据
  2. 使用headless浏览器模拟访问页面,比如可以使用selenium框架实现模拟访问
  3. 通过代理服务获取完整加载后的web页面

Splash是一个javascript渲染服务,提供了代理web访问的功能。

Splash是一个轻量级web浏览器,提供了基于http的访问api。它使用Python3实现,底层基于Twisted和QT5。

Splash具有以下特性:

  1. 可以并行处理多个web页面
  2. 可获取页面的HTML数据,也可以进行页面截图
  3. 可使用Adblock Plus的过滤规则过滤页面上的图片,以加快页面渲染
  4. 可在页面上下文环境中执行javascript脚本
  5. 支持编写lua浏览器脚本
  6. 能够获取HAR格式的详细的渲染信息

我们看一下如何在centos上安装和运行Splash。

首先安装docker,docker版本不低于17.

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

启动docker守护进程

systemctl start docker

获取splash image文件

docker pull scrapinghub/splash

启动splash

docker run -it -p 8050:8050 --rm scrapinghub/splash

 

成功之后,可以访问http://localhost:8050查看splash到底是个什么东东。

 

Splash提供了若干http api:

  1. render.html
    • 返回web页面的HTML数据
    • curl'http://localhost:8050/render.html?url=http://baidu.com/&timeout=10&wait=0.5'
      
  2. render.png
    • 返回web页面的png截图
    • curl ‘http://localhost:8050/render.png?url=http://baidu.com/’
  3. render.har
    • 返回HAR格式的请求交互信息
  4. render.json

在Scrapy中,可以使用scrapy-splash来使用splash的渲染服务,以获取动态页面上的元素。

分类: Python, 开发 标签: , ,

Redis简明入门教程

2020年7月7日 评论已被关闭

【第一节】
Redis是键值(key-value)存储数据库家族中的一员。
何为键值存储?其本质是具备将数据(value)存储到一个键(key)中的能力。这些数据后续可通过并只能通过之前那个key来获取。
通常,Redis被称为数据结构服务器。这是因为,从表面来看,Redis可以以key-value这样简单的形式来存储或获取数据。
但实际上,存储在Redis中的value可以包含复杂的数据结构,比如:字符串、链表、哈希表、集合、有序集合以及适用于概率学的数据结构hyperloglog。

先举个例子。
我们使用set命令将“fido”这个value存储到“server:name”这个key中。
SET server:name “fido”

Redis可以“持久”(依赖于数据过期和持久化策略)存储我们的数据。

这样,我们后续就可以询问Redis:保存在server:name键中的值是什么?
Redis会告诉我们:fido。这通过以下命令实现:
GET server:name
=> “fido”

你可以直接在redis命令行工具redis-cli中输入本文中的命令,=> 代表命令执行后的的输出。

Redis提供用于检测一个key是否存在的命令:
EXISTS server:name
=> 1
EXISTS server:blabla
=> 0
若key存在,返回1;不存在,返回0.

【第二节】
其他基本命令包括:del(删除一个key及存储其中的value)、incr(增加存储在key中数字的数值)。
SET connections 10
INCR connections
=> 11
INCR connections
=> 12
DEL connections
INCR connections
=> 1

incr支持设置每次操作的增量,默认为1。
INCRBY connections 100
=> 101

和incr对应的是decr,用于减少数值型value的值。
DECR connections
=> 100
DECRBY connections 10
=> 90
incr和decr非常适合计数器场景。

【第三节】
也许你会问:incr和decr这样的操作很简单啊,可以通过几行代码就能实现。比如:
x = GET count
x = x + 1
SET count x

确实。但这只适用于单客户端访问一个key的场景。想想一下,如果有两个客户端同时访问这个key呢?
A读取计数器的值为10,同时B也读到了10.
A将计数器的值加1后,将其写回数据库,此时计数器值为11;
B也将计数器的值加1,并将其写回数据库。此时计数器的值仍为11,而不是12.

显然,这种场景之下的计数器并没有起到正确的计数作用。

这是因为上述两个客户端对key的增加操作不是原子性的,需要3步才能完成。
Redis的incr很好的避免了这个问题,它是一个原子操作,一步搞定。

Redis中所有通过单条命令实现的操作都是原子的,包括那些适用于复杂数据结构上的操作。

因此使用Redis命令修改键值时,我们不需要考虑并发访问的问题。

【第四节】
我们向Redis存储key时,可以设置其存活时间,即这个key只在Redis中保留一段时间,过期后就不可再被访问。
这在Redis中通过expire和ttl命令实现。这两个命令的时间单位是秒。还有两个相似的命令,其时间单位是毫秒:pexpire和pttl。
SET resource:lock “Redis Demo”
EXPIRE resource:lock 120

上边,expire将resource:lock的过期时长设为120秒。可以使用ttl查看某个key的剩余存活时长。
TTL resource:lock
=> 113
//113秒之后
TTL resource:lock
=> -2

-2表示这个key不复存在。-1表示表示key永不过期。
注意,若对一个key重新执行set操作,其过期时长将被重置。
SET resource:lock “Redis Demo 1”
EXPIRE resource:lock 120
TTL resource:lock
=> 119

SET resource:lock “Redis Demo 2”
TTL resource:lock
=> -1

set接受额外的参数用于在设置key的值的同时直接设置其过期时长,这“两个”操作是一个原子操作。
SET resource:lock “Redis Demo 3” EX 5
TTL resource:lock
=> 5

Redis也支持取消一个key的过期设置,使其永久存在。这通过persist命令实现。
SET resource:lock “Redis Demo 3” EX 5
PERSIST resource:lock
TTL resource:lock
=> -1

【第五节】
我们开始时已经提到,Redis支持一些复杂的数据结构。现在做下介绍。
第一个就是list。list是一系列的有序值。与list相关的一些重要命令包括:rpush、lpush、llen、lrange、lpop、rpop等。
Redis中的list是可以随手就用的,只要表示list的key不是其他类型。什么意思?
在Redis中,你不需要在使用复杂数据结构之前,先创建一个key然后再为其赋值。你可以直接使用命令来操作这些结构,如果指定的key不存在,Redis会自动创建这个key。
相应的,如果一个key在经历一些操作之后变为空值,这个key也会自动从key空间中被删除。

rpush:向list尾部追加一个元素。
RPUSH friends “Alice”
RPUSH friends “Bob”

lpush:在list头部插入一个元素。
LPUSH friends “Sam”

lrange:获取list元素子集。它的第一个参数是起始元素的索引,第二个参数是结束元素的索引。
第二个参数是-1时,表示一直到list的最后一个(倒数第一个)元素,-2表示倒数第二个元素,以此类推。
LRANGE friends 0 -1
=> 1) “Sam”, 2) “Alice”, 3) “Bob”

LRANGE friends 0 1
=> 1) “Sam”, 2) “Alice”

LRANGE friends 1 2
=> 1) “Alice”, 2) “Bob”

除了添加和获取基于索引的子集之外,list还可以从开头或结尾删除并获取一个元素,即pop操作。
lpop:删除并返回list的第一个元素。
LPOP friends
=> “Sam”

rpop:删除并返回list的最后一个元素。
RPOP friends
=> “Bob”

llen:获取list元素个数。
LLEN friends
=> 1

rpush和lpush参数个数是可变的,我们可以在一条命令中指定多个参数。
RPUSH friends 1 2 3
=> 6
这里,6表示执行push之后list的长度。

【第六节】
接下来,我们介绍set。set和list类似,区别在于,set中的元素是无序的,并且是唯一的。
这两种数据结构都很重要。list可快速访问头部或尾部的元素,set可快速测试元素是否存在。

和set相关的一些重要命令包括:sadd、srem、sismember、smembers和sunion等。

sadd:向set中添加元素,支持可变参数,能同时添加多个元素。
SADD superpowers “flight”
SADD superpowers “x-ray vision” “reflexes”

srem:从set中删除元素,返回值1或0表示元素是否存在于set中。
SREM superpowers “reflexes”
=> 1
SREM superpowers “making pizza”
=> 0

sismember:测试元素是否存在于set中,返回1表示存在,0表示不存在。
SISMEMBER superpowers “flight”
=> 1
SISMEMBER superpowers “reflexes”
=> 0

sunion:计算两个或多个set的并集,返回所有并集中的元素。
SADD birdpowers “pecking”
SADD birdpowers “flight”
SUNION superpowers birdpowers
=> 1) “pecking”, 2) “x-ray vision”, 3) “flight”

sadd的执行结果和srem的结果一样重要。0表明要添加的元素已存在,1表示添加成功。
SADD superpowers “flight”
=> 0
SADD superpowers “invisibility”
=> 1

set也有个和list类似的pop命令,用于从其中删除并返回若干元素。由于set是无序的,被删除并返回的元素也是随机的。
SADD letters a b c d e f
=> 6
SPOP letters 2
=> 1) “c” 2) “a”

spop在key之后的参数为要删除的元素的个数。
set专门有一个命令用于随机返回其中的若干元素,只是返回,不删除。这个命令是srandmember。用法和spop类似,可自行试验。

【第七节】
set是很好用的数据结构,但因为其中的元素是无序的,对某些场景就不太合适了。
Redis 1.2开始引入了有序set:sorted set。
sorted set和set有些相似,区别在于其中的每个元素都关联了个分数(score)。这个分数用于对sorted set中的元素进行排序。

ZADD hackers 1940 “Alan Kay”
ZADD hackers 1906 “Grace Hopper”
ZADD hackers 1953 “Richard Stallman”
ZADD hackers 1965 “Yukihiro Matsumoto”
ZADD hackers 1916 “Claude Shannon”
ZADD hackers 1969 “Linus Torvalds”
ZADD hackers 1957 “Sophie Wilson”
ZADD hackers 1912 “Alan Turing”
这个例子中,集合元素的score是生日年份,元素的值为人名。

我们可以使用zrange获取sorted set中某个范围段的元素。
ZRANGE hackers 2 4
=> 1) “Claude Shannon”, 2) “Alan Kay”, 3) “Richard Stallman”

【第八节】
字符串、list、set和sorted set这些结构已经能处理很多工作了,但Redis还提供了一种数据结构:hash。
hash是字符串字段到字符串值的映射,所以适合表示对象结构。

比如,一个User包含name、surname、age等字段。我们可以用hash存储User的信息。
HSET user:1000 name “John Smith”
HSET user:1000 email “john.smith@example.com”
HSET user:1000 password “s3cret”
我们使用用户标识user:1000作为key,将各个字段依次设置到key中。

可以一次同时设置多个字段值。
HMSET user:1001 name “Mary Jones” password “hidden” email “mjones@example.com”

使用hgetall来获取所有字段。
HGETALL user:1000

也可以每次只获取一个字段的值。
HGET user:1001 name => “Mary Jones”

Redis支持对hash中的某个字段进行incr操作。
HSET user:1000 visits 10
HINCRBY user:1000 visits 1
=> 11
HINCRBY user:1000 visits 10
=> 21

HDEL user:1000 visits
HINCRBY user:1000 visits 1
=> 1

 

【本文译自Redis在线演示教程

分类: Redis 标签: ,

如果MySQL表结构用到了MySQL的关键字,SQL语句该怎么写

2020年7月7日 评论已被关闭

最近在编写一个SQL过程中遇到了如下报错:

ERROR 1064 (42000): You have an error in your SQL syntax

我的SQL语句为:

  insert into t_topic(category, title, desc, content) values(2, "Hello World", 'This is a beautiful world.', 'blabla...');

这是一个很简单的语句,就是往表t_topic里插入一条记录。但是一直不成功。
首先检查了一下各字段数据类型,都是匹配的。
然后注意到报错中提示是在’desc’字段出了错。

从网上查了一下MySQL这个错误码。恍然大悟。
原来语句中使用到了MySQL的关键字desc!
虽然我经常用desc查看表结构,但没意识到这个表结构竟然用它作为字段名称了!
这种表设计真是太不应该了。

解决办法:在sql语句中,用到MySQL关键字的地方,加上反引号,也就是键盘上数字那一排最右侧那个(“)。如下所示:

  insert into t_topic(category, title, ·desc·, content) values(2, "Hello World", 'This is a beautiful world.', 'blabla...');

			
分类: mysql 标签:

使用python调用谷歌翻译接口实现英文到中文的翻译

2020年6月27日 评论已被关闭

通过抓包可以看到谷歌的翻译接口需要生成一个token,此功能需要通过js实现。

我们可以使用PyExecJs这个库来实现在python中执行js脚本的功能。

然后直接通过requests调用谷歌翻译接口就行了。

代码如下:

import execjs  
import requests
  
class Py4Js():  
    def __init__(self):  
        self.ctx = execjs.compile(""" 
        function TL(a) { 
        var k = ""; 
        var b = 406644; 
        var b1 = 3293161072; 
         
        var jd = "."; 
        var $b = "+-a^+6"; 
        var Zb = "+-3^+b+-f"; 
     
        for (var e = [], f = 0, g = 0; g < a.length; g++) { 
            var m = a.charCodeAt(g); 
            128 > m ? e[f++] = m : (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023), 
            e[f++] = m >> 18 | 240, 
            e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224, 
            e[f++] = m >> 6 & 63 | 128), 
            e[f++] = m & 63 | 128) 
        } 
        a = b; 
        for (f = 0; f < e.length; f++) a += e[f], 
        a = RL(a, $b); 
        a = RL(a, Zb); 
        a ^= b1 || 0; 
        0 > a && (a = (a & 2147483647) + 2147483648); 
        a %= 1E6; 
        return a.toString() + jd + (a ^ b) 
    }; 
     
    function RL(a, b) { 
        var t = "a"; 
        var Yb = "+"; 
        for (var c = 0; c < b.length - 2; c += 3) { 
            var d = b.charAt(c + 2), 
            d = d >= t ? d.charCodeAt(0) - 87 : Number(d), 
            d = b.charAt(c + 1) == Yb ? a >>> d: a << d; 
            a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d 
        } 
        return a 
    } 
    """)  
          
    def getTk(self,text):  
        return self.ctx.call("TL",text)


def google_translate(content):
    '''实现谷歌的翻译'''
    js = Py4Js()
    tk = js.getTk(content)

    if len(content) > 4891:      
        print("翻译的长度超过限制!!!")      
        return    
  
    param = {'tk': tk, 'q': content}  
  
    result = requests.get("""http://translate.google.cn/translate_a/single?client=t&sl=en 
        &tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss 
        &dt=t&ie=UTF-8&oe=UTF-8&clearbtn=1&otf=1&pc=1&srcrom=0&ssel=0&tsel=0&kc=2""", params=param)  
  
    #返回的结果为Json,解析为一个嵌套列表  
    trans = result.json()[0]
    ret = ''
    for i in range(len(trans)):
        line = trans[i][0]
        if line != None:
            ret += trans[i][0]

    return ret

ret = google_translate('this is a test of translating from english to chinese')
print(ret)

本文参考git上的TranslateTool这个项目。

分类: Python, 开发 标签: , ,

在centos7上手动搭建lnmp环境

2020年6月20日 评论已被关闭

LNMP 环境是指在 Linux 系统下,由 Nginx + MySQL/MariaDB + PHP 组成的网站服务器架构。

本文档介绍如何在腾讯云云服务器(CVM)上手动搭建 LNMP 环境。

进行手动搭建 LNMP 环境,您需要熟悉 Linux 命令,例如 CentOS 环境下通过 YUM 安装软件 等常用命令,并对所安装软件的使用及版本兼容性比较了解。

分类: mysql, Php, 开发 标签: ,

基于kite的自动化测试管理系统(KTMS)设计方案

2020年6月12日 评论已被关闭

我们之前介绍了webrtc自动化测试框架kite的基本概念。了解到以下两点:

  • kite由4个主要组件构成
  • kite通过配置文件来指定测试用例脚本和参数

我们通过r命令来运行测试用例。对于一个实用的测试用例来讲,测试数据或者说用例参数应该可以灵活指定,而不能写死在配置文件中,或者每次通过手动修改配置文件来运行不同的测试数据。

Kite整合的allure是一个非常好的测试报表,但是也并不能完全满足所有需求。它只是从日志中提取了执行步骤和结果,也没有太多可供分析的数据。

对于自动化测试,我们希望可以是无人值守的,跑完用例就能自动得到结果。而Allure需要你估计一下用例运行时间,然后自己去刷一下页面,看一下结果。

显然,上述这些需求都需要在Kite基础上进行定制开发。

如果你深入理解了kite的框架构成和运行原理,就可以对其进行定制开发。

KTMS是笔者基于上述需求对Kite做的二次开发,是一套可视化测试管理系统,包含以下功能:

  • 可以配置测试账号、测试接口、浏览器版本等测试数据
  • 可以通过组合测试步骤的方式配置测试用例,手动(点一下)或自动(定时任务)运行测试用例
  • 可以自动收集测试数据,包括当前执行步骤、失败原因等,可通过钉钉发送测试结果

设计思路为:

  • 给测试人员提供配置界面,可添加和修改测试数据
  • 对测试过程进行抽象,提取出相关测试步骤,提供开关以决定是否运行
  • 可以由测试用例生成测试任务
  • 在selenium hub节点,部署一个agent,监听测试任务
  • agent获取测试任务对应的测试配置,根据需要修改kite配置文件,并运行新的用例
  • 测试用例脚本在启动时从数据库(MySQL及redis)读取所需数据,并上报每个步骤的结果数据到数据库
  • 测试任务执行完成后,更新数据库中的任务状态,并通过钉钉发送通知给测试人员

技术栈:

  • python、flask
  • MySQL、redis
  • nodejs
分类: 开发, 测试 标签: ,