Scylla 中文文档

最后更新于:2018-05-30 03:51:44

Scylla 是一款高质量的免费代理 IP 池工具,仅支持 Python 3.6。特性如下:

自动化的代理 IP 爬取与验证
易用的 JSON API
简单但美观的 web 用户界面,基于 TypeScript 和 React(例如,代理的地理分布)
最少仅用一条命令即可启动
简明直接的编程 API(将在 1.1 版本中加入)
无头浏览器(headless browser crawling)爬虫

快速开始
安装
Docker 安装(推荐)

docker run -d -p 8899:8899 -v /var/www/scylla:/var/www/scylla --name scylla wildcat/scylla:latest

使用 pip 直接安装

pip install scylla
scylla --help
scylla # 运行爬虫和 Web 服务器

从源代码安装

git clone https://github.com/imWildCat/scylla.git
cd scylla

pip install -r requirements.txt

npm install # 或 yarn install
make build-assets

python -m scylla

使用

这里以服务运行在本地(localhost)为例,使用口号 8899。 注意:首次运行本项目时,您可能需要等待 1~2 分钟以爬取一定量的代理 IP。
JSON API
代理 IP 列表

http://localhost:8899/api/v1/proxies

可选 URL 参数:
参数 默认值 说明
page 1 页码
limit 20 每页显示代理 IP 的数量
anonymous any 是否显示匿名代理。可选值:true,只显示匿名代理;false,只显示透明代理。
countries 无 只选取特定国家的代理,格式示例:US,或者多国家:US,GB

结果样例:

{
"proxies": [{
"id": 3661,
"ip": "118.114.77.47",
"port": 8080,
"is_valid": true,
"created_at": 1527312259,
"updated_at": 1527351023,
"latency": 250.9789636882,
"stability": 1.0,
"is_anonymous": true,
"location": "29.3416,104.7770",
"organization": "AS4134 CHINANET-BACKBONE",
"region": "Sichuan",
"country": "CN",
"city": "Zigong"
}, {
"id": 3657,
"ip": "39.104.57.121",
"port": 8080,
"is_valid": true,
"created_at": 1527312253,
"updated_at": 1527351021,
"latency": 189.1011954867,
"stability": 0.2,
"is_anonymous": true,
"location": null,
"organization": null,
"region": null,
"country": null,
"city": null
},
...
],
"count": 1025,
"per_page": 20,
"page": 1,
"total_page": 52
}

系统统计

http://localhost:8899/api/v1/stats

结果样例:

{
"median": 181.2566407083,
"valid_count": 1780,
"total_count": 9528,
"mean": 174.3290085201
}

Web 界面

打开 http://localhost:8899 即可访问本项目的 Web 界面。
代理 IP 列表

http://localhost:8899/

截图:

screenshot-proxy-list
代理 IP 全球分布

http://localhost:8899/#/geo

截图:

screenshot-geo-distribution
API 文档

请阅读 模块索引。更易用的编程接口正在开发中。
开发路线图

请查看 Projects。
开发与贡献

git clone https://github.com/imWildCat/scylla.git
cd scylla

pip install -r requirements.txt

npm install # 或 `yarn install`
make build-assets

测试

本项目使用了较多的单元测试来保证代码的质量,并集成 Travis CI 来实现持续集成。如需在本地运行测试,命令如下:

pip install -r tests/requirements-test.txt
pytest tests/

 

https://scylla.wildcat.io/zh/latest/

MFC入门(五)– 新建子窗口,添加子窗口初始化虚函数,修改版本和图标

最后更新于:2019-02-15 01:26:50

最近在用MFC的过程中遇到的一些小TIP总结:

1.    新建子窗口:

在创建的Dialog上右击添加类,取名为子窗口。

打开Resource.h我们可以看到IDD——DIALOG1的属性值是131

双击主窗口的确定按钮,添加如下初始化显示子窗口的代码:”

    子窗口 *一个窗口 = new 子窗口();

    一个窗口->Create(131,this);//这里131代表的是子窗口的属性值,也可以直接把Resource.h包括进来用IDD_DIALOG1

    一个窗口->ShowWindow(SW_SHOW);

 

 

2.  子窗口初始化:

通过在类向导里添加虚函数OnInitDialog()

在OnInitDialog()添加对应代码就可以实现对应的初始化功能了。

例如在初始化里加一段弹出消息的代码:

MessageBoxW(TEXT("❤koala睡不醒"), TEXT("嘿嘿嘿"));

运行结果如下:

3.  子窗口功能按键:

和主窗口类似,双击就可以定位到指定地方……

4.    修改版本,也就是鼠标悬停在上方时的信息:

在资源视图里。

5    修改图标:

在项目的res目录下替换掉对应ico图标文件即可。

MFC入门(四)– 新建线程+按键响应(计算出生天数+打开掏宝小Demo)

最后更新于:2019-02-15 01:27:26

遇到的问题以及关键点总结下:

1.打开链接的函数:

[cpp] view plain copy

  1. ShellExecute(NULL, _T("open"), _T("www.taobao.com"), NULL, NULL, SW_SHOWNORMAL);

2.显示时间的代码部分:

[cpp] view plain copy

  1. SYSTEMTIME sys;
  2. CString txt_show;
  3. GetLocalTime(&sys);
  4. txt_show.Format(L"%4d/%02d/%02d %02d:%02d:%02d 星期%1d", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wDayOfWeek);
  5. SetDlgItemText(IDC_EDIT2, txt_show);

3.计算时间的代码部分://主要用到CTimeSpan这个类,用起来非常顺畅!

[cpp] view plain copy

  1. CTime time1 = CTime::GetCurrentTime();
  2. CTime m_begintime = CTime(1992, 10, 5, 0, 30, 11, -1);//参数依次为year,month,day,hour,minite,second
  3. CTime m_endtime = CTime::GetCurrentTime();//当前时间
  4. CTimeSpan span;
  5. span = m_endtime - m_begintime;
  6. txt_show.Format(L"猫女破壳 %I64d天  总计%I64d秒", span.GetDays(), span.GetTotalSeconds());
  7. SetDlgItemText(IDC_EDIT3, txt_show);

 

4.GetDays()等函数返回的值是longlong类型,故而转化的时候需要写成%I64d否则写多个%d的时候会篡位……(还没搞清楚原理…)

5.设置按键响应的方法:

在类向导里添加PreTranslateMessage重写如下(具体代码和按键稍微看下就知道啦)

[cpp] view plain copy

  1. BOOL CMFCApplication1Dlg::PreTranslateMessage(MSG* pMsg)
  2. {
  3.     if (pMsg->message == WM_KEYDOWN)
  4.     {
  5.         switch (pMsg->wParam)
  6.         {
  7.         case VK_ESCAPE:
  8.             CDialogEx::OnCancel();
  9.             return true;
  10.             break;
  11.         }
  12.     }
  13.     return CDialogEx::PreTranslateMessage(pMsg);
  14. }

 

6.定一个了一个实时刷新显示当前系统时间的函数:

//这里很无脑的写了一个While(1), 这是非常不好的,调试用ESC关闭会出现问题,但是生成EXE不会影响,所以就不管了,233

解决办法很简单,在类里定义一个变量来检测ESC按键,检测到了就退出循环,这里的while(1)换成while(bool)就好

[cpp] view plain copy

  1. void CMFCApplication1Dlg::调用时间() {
  2.         while(1){
  3.         SYSTEMTIME sys;
  4.         CString txt_show;
  5.         GetLocalTime(&sys);
  6.         txt_show.Format(L"%4d/%02d/%02d %02d:%02d:%02d 星期%1d", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wDayOfWeek);
  7.         SetDlgItemText(IDC_EDIT2, txt_show);
  8.         CTime time1 = CTime::GetCurrentTime();
  9.         CTime m_begintime = CTime(1992, 10, 5, 0, 30, 11, -1);//参数依次为year,month,day,hour,minite,second
  10.         CTime m_endtime = CTime::GetCurrentTime();//当前时间
  11.         CTimeSpan span;
  12.         span = m_endtime - m_begintime;
  13.         txt_show.Format(L"猫女破壳 %I64d天  总计%I64d秒", span.GetDays(), span.GetTotalSeconds());
  14.         SetDlgItemText(IDC_EDIT3, txt_show);
  15.         m_begintime = CTime(2016, 11, 21,23, 10, 0, 0);
  16.         span = m_endtime - m_begintime;
  17.         txt_show.Format(L"在一起了:❤%I64d天 %2d小时 %2d分 %2d秒❤", span.GetDays(), span.GetHours(), int(span.GetMinutes()), int(span.GetSeconds()));
  18.         SetDlgItemText(IDC_EDIT6, txt_show);
  19.         m_begintime = CTime(1992, 12, 14, 3, 30, 0, -1);
  20.         span = m_endtime - m_begintime;
  21.         txt_show.Format(L"犬男破壳 %I64d天  总计%I64d秒", span.GetDays(), span.GetTotalSeconds());
  22.         SetDlgItemText(IDC_EDIT5, txt_show);
  23.         Sleep(1000);
  24.         }
  25.     }

 

7.重头戏!!!在类里定义一个thread

首先当然需要#include<thread>  //多么方便的C++11线程

接着在生成的类里添加变量 thread 线程1

[cpp] view plain copy

  1. std::thread 线程1;

在界面初始化的时候给线程赋值:

[cpp] view plain copy

  1. BOOL CMFCApplication1Dlg::OnInitDialog()
  2. {
  3.     CDialogEx::OnInitDialog();
  4.     // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
  5.     //  执行此操作
  6.     SetIcon(m_hIcon, TRUE);         // 设置大图标
  7.     SetIcon(m_hIcon, FALSE);        // 设置小图标
  8.     // TODO: 在此添加额外的初始化代码
  9.      线程1 = thread([&] {调用时间(); });
  10.     return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
  11. }

 

.h文件如下

[cpp] view plain copy

  1. #pragma once
  2. #include<thread>  
  3. // CMFCApplication1Dlg 对话框
  4. class CMFCApplication1Dlg : public CDialogEx
  5. {
  6. // 构造
  7. public:
  8.     CMFCApplication1Dlg(CWnd* pParent = NULL);  // 标准构造函数
  9. // 对话框数据
  10. #ifdef AFX_DESIGN_TIME
  11.     enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
  12. #endif
  13.     protected:
  14.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
  15. // 实现
  16. protected:
  17.     HICON m_hIcon;
  18.     // 生成的消息映射函数
  19.     virtual BOOL OnInitDialog();
  20.     afx_msg void OnPaint();
  21.     afx_msg HCURSOR OnQueryDragIcon();
  22.     DECLARE_MESSAGE_MAP()
  23. public:
  24.     std::thread 线程1;
  25.     virtual BOOL PreTranslateMessage(MSG* pMsg);
  26.     afx_msg void OnBnClickedCancel();
  27. //  afx_msg void OnEnChangeEdit1();
  28.     afx_msg void OnBnClickedOk();
  29.     void 调用时间();
  30.     afx_msg void OnBnClickedButton1();
  31. };

8.换图标的方法,自己做一个ico文件,替换掉res文件目录下的图标就好

所有代码如下:http://download.csdn.net/detail/zmdsjtu/9693610

效果图如下:

MFC入门(三)– MFC图片/文字控件(循环显示文字和图片的小程序)

最后更新于:2019-02-15 01:27:32

首先新建一个MFC项目,习惯性选择静态库

右击MFC_Picture.rc选择添加资源,把需要添加显示的BMP图片文件添加到资源,本文为了滚动显示两张图片,所以只用了两张BMP,当然你也可以三张四张无数张,在待会提及的代码部分进行修改即可。

 

会默认生成两个BITMAP,名字就用的默认的BITMAP1和BITMAP2,在代码部分可以对应看下。

 

选择Bitmap导入,在对应目录下选择所有文件,选择两张bmp文件导入即可。

 

现在我们就有了两张可以用的Bitmap了。

 

接着我们搭建界面如下:

图里没见过的那个小仙人掌的图标就是我们今天要用的图片Bitmap控件,选择左侧工具栏里的Picture Control拖动到右侧,再在属性里把Type改成本文要用的Bitmap,并讲名字重命名为IDC_show(因为默认的是Static在添加显示图片变量时会出问题,所以这里必须要改!!)

继而右击小仙人掌添加变量m_show用以一会儿动态显示图片~

//图里的m_show是黑的是因为之前已经添加了,不必在意,打上去就好

 

 

然后就是关键性的代码阶段了,双击下一张的按钮空降到指定代码位置,复制代码如下:

 

 

int i = 0;  //变量i用来切换图片以及文字

void CMFC_PictureDlg::OnBnClickedOk()

{

 

CString txt_show;

CBitmap bitmap;  // CBitmap对象,用于加载位图

HBITMAP hBmp;    // 保存CBitmap加载的位图的句柄

if (i == 0) {

bitmap.LoadBitmap(IDB_BITMAP1);  // 将位图IDB_BITMAP1加载到bitmap

txt_show.Format(_T("你瞅啥?")); SetDlgItemText(IDC_EDIT1, txt_show);

}

if (i == 1) {

bitmap.LoadBitmap(IDB_BITMAP2);

txt_show.Format(_T("再瞅试试?")); SetDlgItemText(IDC_EDIT1, txt_show);

}

 

 

i++;

if (i == 2) i = 0;

hBmp = (HBITMAP)bitmap.GetSafeHandle();  // 获取bitmap加载位图的句柄

m_show.SetBitmap(hBmp);    // 设置图片控件m_jzmPicture的位图图片为IDB_BITMAP1

}

 

 

下面阐述下最为关键的两个模块:

第一个是显示BMP文件的部分:

 

bitmap.LoadBitmap(IDB_BITMAP1); 将图片载入之后

hBmp = (HBITMAP)bitmap.GetSafeHandle();  获取bitmap加载位图的句柄

m_show.SetBitmap(hBmp);   最后显示图片到之前新建的图片控件的变量上

 

第二块是文字显示的模块:

 

新建CString txt_show;

 

txt_show.Format(_T("你瞅啥?")); 给txt_show赋值

SetDlgItemText(IDC_EDIT1, txt_show);显示在ID为IDC_EDIT1的文字控件上

 

 

最后是结果展示~~~  //并不精神污染

 

 

 

最后祝大家编程愉快~~

MFC入门(二)– 提取输入框的字符串(定时关机的小程序)

最后更新于:2019-02-15 01:27:38

在之前完成的第一个MFC小程序的基础上来进行第二个的撰写。

打开Dialog之后,首先添加Edit Control控件用于输入数据,修改其属性里的Number为True,这样这个编辑框就只能输入整数啦~可以看到这个控件的ID为IDC_EDIT1,也就是代表了这个控件的地址,如果想访问它就必须得知道这个地址。

 

整体布局大概如下:   //无关痛痒

 

下面就是对于关机Button对应的代码,双击关机Button到对应的函数位置,复制以下代码:

 

void CFirst_MFCDlg::OnBnClickedButton3()

{

int iResult = ::MessageBox(NULL, TEXT("确认要关机?"), TEXT("关机"), MB_OKCANCEL | MB_ICONQUESTION);

if (1 == iResult)

{

CString str;

((CEdit *)GetDlgItem(IDC_EDIT1))->GetWindowText(str);//获得

CString Shut = _T("shutdown   -s   -t ");     //左部分红色固定的字符串

str = Shut + str;

const size_t strsize = (str.GetLength() + 1) * 2; // 宽字符的长度;

char * pstr = new char[strsize]; //分配空间;

size_t sz = 0;

wcstombs_s(&sz, pstr, strsize, str, _TRUNCATE);

int n = atoi((const char*)pstr); // 字符串已经由原来的CString 转换成了 const char*

system(pstr);

//system("shutdown -s -t 15");

}

}

 

这里用到一个关键的功能函数system("shutdown -s -t 15")其主要作用相当于CMD里直接输hutdown -s -t 15,也就是十五秒后关机。需要做的工作也就是把15替换成从IDC_EDIT1读取的数据即可。

 

 

关键的提取输入字符的代码:

((CEdit *)GetDlgItem(IDC_EDIT1))->GetWindowText(str);

函数表示从控件读取String,但注意的是system()里的变量类型是const char*,而这里是CString类型,故而有了中间一长段由CString转const char*的代码。

 

此外为了防止测试的时候手画所以在点击关机按钮时首先会有弹出窗提示是否关机,具体功能各位看管可根据自己的实际需求来进行调整。

 

为防止手抖特意加了取消关机的按钮,其功能函数如下:

void CFirst_MFCDlg::OnBnClickedButton4()

{

// TODO: 在此添加控件通知处理程序代码

system("shutdown -a");

}

 

最后实现结果如下:

 

 

最后祝大家编程愉快~

MFC入门(一)– 第一个简单的windows图形化界面小程序(打开计算器,记事本,查IP)

最后更新于:2019-02-15 01:27:44

惯例放上编程所用软件VS2015下载地址:https://www.visualstudio.com/

//VS2010,VS2012也是类似的

值得注意的是这里采用C++编译,安装时务必勾选编程语言里的C++,如果不慎没安装,之后新建项目选择C++项目会提示安装,但会等挺久的

 

////////////////////////////正式开工/////////////////////////

 

第一步,新建项目  //新建项目--Visual C++ --MFC --MFC应用程序

 

 

 

 

这里比较习惯用基于对话框的MFC界面,要注意下右下角的MFC的使用,如果是在本机上可以选择动态库Dll的模式,如果想在别人并没有你的环境的情况下(比如给爸妈使用)则最好选择静态库中使用MFC,这样就相当于把dll也封装进了程序,从而程序会比DLL的略大一点~

 

到这步可以直接点完成然后生成如下界面:

首先改下说明,点击图片中的文字,在属性栏中找到对应的Caption便可以修改文字,这里准备做一个简单的打开计算器的功能。

 

 

下面我们来给按钮加上打开计算器的功能。

首先左击确定按钮看到属性栏的Caption改成“打开计算器”如图:

 

双击“打开计算器”按钮便可以跳转到按钮对应的功能定义位置进行代码的撰写。

 

修改代码如下:

void CFirst_MFCDlg::OnBnClickedOk()

{

// TODO: 在此添加控件通知处理程序代码

HINSTANCE hRslt = ShellExecute(NULL, _T("open"),

_T("calc.exe"),NULL,NULL,SW_SHOWNORMAL);

assert(hRslt > (HINSTANCE)HINSTANCE_ERROR);

}

 

之后需要在这个文件的最上方#include <assert.h>方便调用打开计算器的程序。

 

//////////////////到了这里可以测试下程序了~生成-生成解决方案,在release里有可以点开使用的EXE文件//////////

 

如果想添加新的控件,点击左侧的工具箱,例如我想加一个新的按钮来增加一个打开记事本的功能,就拖动button到界面上,修改名字成“打开记事本”

 

对应粘贴代码如下:

 

void CFirst_MFCDlg::OnBnClickedButton1()

{

// TODO: 在此添加控件通知处理程序代码

HINSTANCE hRslt = ShellExecute(NULL, _T("open"),

_T("notepad.EXE"),NULL,NULL,SW_SHOWNORMAL);

assert(hRslt > (HINSTANCE)HINSTANCE_ERROR);

}

 

再加上一个查看IP地址的button以及对应的代码如下:

 

 

void CFirst_MFCDlg::OnBnClickedButton2()

{

// TODO: 在此添加控件通知处理程序代码

// TODO: 在此添加控件通知处理程序代码Nslookup

HINSTANCE hRslt = ShellExecute(NULL, _T("open"),

_T("Nslookup.EXE"),NULL,NULL,SW_SHOWNORMAL);

assert(hRslt > (HINSTANCE)HINSTANCE_ERROR);

}

 

然后选择Release并生成解决方案,在项目对应的Release里可以看到生成的可以使用的EXE啦~

默认的目录如下:

 

实现结果如下: //一个可以发给爸妈使用的程序就完成啦~

 

最后祝大家编程愉快~

BadTouch:一个可编写脚本的网络身份验证破解程序

最后更新于:2018-05-02 09:17:38

badtouch是一个可编写脚本的网络身份验证破解程序。虽然如今市面上关于常见服务爆破的程序已经接近于饱和的状态,但在实际Web应用凭据测试时,你仍可能最终会自己手动来编写自己的Python脚本。

badtouch的范围是定制服务破解。这是通过编写被加载到lua运行的脚本来完成的。这些脚本代表单个服务,并提供返回true或false的verify(user, password)功能。badtouch运行当中会为我们提供并发性,进度指示和报告信息。

视频演示:

<iframe src="https://v.qq.com/iframe/player.html?vid=d0626bq8ssc&amp;tiny=0&amp;auto=0&amp;width=640&amp;height=498&amp;width=270&amp;height=200" width="640" height="450" frameborder="0"></iframe>

base64_decode

解码base64字符串。

base64_decode("ww==")

base64_encode

用base64编码一个二进制数组。

base64_encode("\x00\xff")

execve

执行一个外部程序。返回退出代码。

execve("myprog", {"arg1", "arg2", "--arg", "3"})

hex

十六进制编码一个字节列表。

hex("\x6F\x68\x61\x69\x0A\x00")

hmac_md5

用md5计算hmac。返回一个二进制数组。

hmac_md5("secret", "my authenticated message")

hmac_sha1

用sha1计算hmac。返回一个二进制数组。

hmac_sha1("secret", "my authenticated message")

hmac_sha2_256

用sha2_256计算hmac。返回一个二进制数组。

hmac_sha2_256("secret", "my authenticated message")

hmac_sha2_512

用sha2_512计算hmac。返回一个二进制数组。

hmac_sha2_512("secret", "my authenticated message")

hmac_sha3_256

用sha3_256计算hmac。 返回一个二进制数组。

hmac_sha3_256("secret", "my authenticated message")

hmac_sha3_512

用sha3_512计算hmac。 返回一个二进制数组。

hmac_sha3_512("secret", "my authenticated message")

html_select

解析一个html文档并返回匹配CSS选择器的第一个元素。返回值是一个表格,其中text为内部文本,attrs是元素属性的表格。

csrf = html_select(html, 'input[name="csrf"]')
token = csrf["attrs"]["value"]

html_select_list

与html_select相同,但返回所有匹配,而不是第一个匹配。

html_select_list(html, 'input[name="csrf"]')

http_basic_auth

使用基本身份验证发送GET请求。如果没有设置WWW-Authenticate头并且状态码不是401,则返回true。

http_basic_auth("https://httpbin.org/basic-auth/foo/buzz", user, password)

http_mksession

创建一个session对象。这与python-request中的requests.Session类似,并且会跟踪cookie。

session = http_mksession()

http_request

准备一个http请求。第一个参数是从该会话被复制到请求中的会话引用和cookies。发送请求后,响应中的cookie将被复制回会话中。

下一个参数是method,url和其他选项。请注意,即使没有设置选项,你仍然需要指定一个空表{}。有以下选项可用:

  • query - 应该在url上设置的查询参数的映射
  • headers - 应该设置的头映射
  • basic_auth - (未实现)使用{“user, “password”}配置基本auth标头}
  • user_agent - 用字符串覆盖默认的用户代理
  • json - 应该被json编码的请求体
  • form - 应该进行表单编码的请求体
  • body - 作为字符串的原始请求体
req = http_request(session, 'POST', 'https://httpbin.org/post', {
    json={
        user=user,
        password=password,
    }
})
resp = http_send(req)
if last_err() then return end
if resp["status"] ~= 200 then return "invalid status code" end

http_send

发送使用http_request构建的请求。使用以下键返回表格:

  • status - http状态码
  • headers - 表格头
  • text - 响应体作为字符串
req = http_request(session, 'POST', 'https://httpbin.org/post', {
    json={
        user=user,
        password=password,
    }
})
resp = http_send(req)
if last_err() then return end
if resp["status"] ~= 200 then return "invalid status code" end

json_decode

从json字符串解码lua值。

json_decode("{\"data\":{\"password\":\"fizz\",\"user\":\"bar\"},\"list\":[1,3,3,7]}")

json_encode

将lua值编码为json字符串。请注意,空表格被编码为空对象{}而不是空列表[]。

x = json_encode({
    hello="world",
    almost_one=0.9999,
    list={1,3,3,7},
    data={
        user=user,
        password=password,
        empty=nil
    }
})

last_err

如果未记录错误,则返回nil,否则返回字符串。

if last_err() then return end

ldap_bind

连接到ldap服务器并尝试使用给定的用户进行身份验证。

ldap_bind("ldaps://ldap.example.com/",
    "cn=\"" .. ldap_escape(user) .. "\",ou=users,dc=example,dc=com", password)

ldap_escape

以相对专有名称转义属性值。

ldap_escape(user)

ldap_search_bind

连接到ldap服务器,登录到用户搜索,搜索目标用户并尝试使用搜索返回的第一个DN进行身份验证。

ldap_search_bind("ldaps://ldap.example.com/",
    -- the user we use to find the correct DN
    "cn=search_user,ou=users,dc=example,dc=com", "searchpw",
    -- base DN we search in
    "dc=example,dc=com",
    -- the user we test
    user, password)

md5

使用md5对字节数组哈希,并将返回结果作为字节。

hex(md5("\x00\xff"))

mysql_connect

连接到一个MySQL数据库,并尝试使用提供的凭据进行身份验证。成功时返回true。

mysql_connect("127.0.0.1", 3306, user, password)

print

打印一个变量的值。请注意为了避免带来不必要的麻烦,建议仅在调试时使用。

print({
    data={
        user=user,
        password=password
    }
})

rand

返回具有最小和最大约束的随机u32。返回值可以大于或等于最小边界,并始终低于最大边界。此功能尚未经过密码安全审查。

rand(0, 256)

randombytes

生成指定数量的随机字节。

randombytes(16)

sha1

使用sha1对一个字节数组哈希,并将返回结果作为字节。

hex(sha1("\x00\xff"))

sha2_256

使用sha2_256对一个字节数组哈希,并将返回结果作为字节。

hex(sha2_256("\x00\xff"))

sha2_512

使用sha2_512对一个字节数组哈希,并将返回结果作为字节。

hex(sha2_512("\x00\xff"))

sha3_256

使用sha3_256对一个字节数组哈希,并将返回结果作为字节。

hex(sha3_256("\x00\xff"))

sha3_512

使用sha3_512对一个字节数组哈希,并将返回结果作为字节。

hex(sha3_512("\x00\xff"))

sleep

暂停该线程指定秒数。这主要用于调试并发性。

sleep(3)

配置

你可以在~/.config/badtouch.toml中放置一个配置文件来设置一些默认值。

Global user agent

[runtime]
user_agent = "w3m/0.5.3+git20180125"

RLIMIT_NOFILE

[runtime]
# requires CAP_SYS_RESOURCE
# sudo setcap 'CAP_SYS_RESOURCE=+ep' /usr/bin/badtouch
rlimit_nofile = 64000

编译python脚本

badtouch运行时仍然非常的简陋,因此你可能需要对常规Python脚本进行编译。你的wrapper可能是这样的:

descr = "example.com"

function verify(user, password)
    ret = execve("./docs/test.sh", {user, password})
    if last_err() then return end

    if ret == 2 then
        return "script signaled an exception"
    end

    return ret == 0
end

你的python脚本可能是这样的:

import sys

try:
    if sys.argv[1] == "foo" and sys.argv[2] == "bar":
        # correct credentials
        exit(0)
    else:
        # incorrect credentials
        exit(1)
except:
    # signal an exception
    # this requeues the attempt instead of discarding it
    exit(2)

前言

最后更新于:2018-02-07 03:15:53

译者的话

人生苦短,我用Python的!

Python中的未来需要每个人的帮助和支持。目前市面上的教程书籍,网上的手册大部分基本都是2.X系列的,专门基于3.X系列的书籍少的可怜。

最近看到一本“Python Cookbook”3rd Edition,完全基于Python 3,写的也很不错。为了Python 3的普及,我也不自量力,想做点什么事情。于是乎,就有了翻译这本书的冲动了这不是一项轻松的工作,却是一件值得做的工作:不仅方便了别人,而且对自己翻译能力也是一种锻炼和提升。

译者会坚持对自己每一句的翻译负责,力求高质量。但受能力限制,也难免有疏漏或者表意不当的地方。如果译文中有什么错漏的地方请大家见谅,也欢迎大家随时指正:yidao620 @ gmail.com

作者的话

自从2008年以来,Python 3横空出世并慢慢进化.Python 3的流行一直被认为是需要很长一段时间。事实上,到我写这本书的2013年,绝大部分的Python程序员仍然在生产环境中使用的是版本2系列,最主要是因为Python 3不向后兼容。毫无疑问,对于工作在遗留代码上的每个程序员来讲,向后兼容是不得不考虑的问题。但是放眼未来,你就会发现Python 3给你带来不一样的惊喜。

例如Python 3代表未来一样,新的“Python Cookbook”版本相比较之前的版本已经有了一个全新的改变。代码都是在Python 3.3版本下面编写和测试的,并没有考虑之前老版本的兼容性没有标注旧版本下的解决方案。这样子可能会有争议,但是我们最终的目的是写一本完全基于现代工具和语言的书籍。我们希望本书能够指导人们使用Python 3编写新的代码或者升级之前的遗留代码。

毫无疑问,编写一本这样的书给编辑工作带来一定的挑战。如果在网上搜索Python的秘密的话,会在诸如ActiveState的Python食谱或者Stack Overflow的网站上搜到数以千计的有用的秘籍,但是其中绝大部分已经是过时的了。这些秘籍除了是基于Python 2编写之外,可能还有很多解决方案在不同的版本之间是不一样的(比如2.3和2.4版本)。另外,它们还会经常使用一些过时的技术,这些可能已经内置到Python 3.3里面去了。寻找完全基于Python 3的秘籍真的难上加难啊。

这本书的所有主题都是基于已经存在的代码和技术,而不是专门去寻找 Python 3 特有的秘籍。 在原有代码基础上,我们完全使用最新的 Python 技术去改造。 所以,任何想使用最新技术编写代码的程序员,都可以将本书当做一本很好的参考书籍。

在选择要包含哪些秘籍方面,很明显不可能编写一本书囊括 Python 领域所有的东西。 因此,我们优先选择了 Python 语言核心部分,以及那些有着广泛应用领域的问题。 另外,其中有很多秘籍用来展示 Python 3 的新特性, 这对于很多人来说是比较陌生的,哪怕是使用 Python 老版本的经验丰富的程序员。 这些示例程序也会偏向于展示一些有着广泛应用的编程技术 (即编程模式), 而不是仅仅定位在一些具体的问题上。尽管也提及到了一些第三方包,但是本书主要定位在 Python 语言核心和标准库。

这本书适合谁

这本书的目标读者是那些想深入理解 Python 语言机制和现代编程风格的有经验的 Python 程序员。 本书大部分内容集中于在标准库,框架和应用程序中广泛使用的高级技术。 本书所有示例均假设读者具有一定的编程背景并且可以读懂相关主题 (比如基本的计算机科学知识,数据结构知识,算法复杂度,系统编程,并行,C 语言编程等)。 另外,每个示例都只是一个入门指导,如果读者想深入研究,需要自己去查阅更多资料。 我们假定读者可以很熟练的使用搜索引擎以及知道怎样查询在线的 Python 文档。

有一些更加高级的秘籍,如果耐心阅读,将有助于理解 Python 底层的工作原理。 从中你将学到一些新的技巧和技术,并应用到你自己的代码中去。

这本书不适合谁

这本书不适合 Python 的初学者。事实上,本书假定读者具有 Python 教程或入门书籍中所教授的基础知识。 本书也不是那种快速参考手册 (例如快速查询某个模块下的某个函数)。 本书旨在聚焦几个最重要的主题,演示几种可能的解决方案, 提供一个跳板引导读者进入一些更高级的内容(这些可以在网上或者参考手册中找到)。

在线示例代码

本书几乎所有源代码均可以在 http://github.com/dabeaz/python-cookbook 上面找到。 作者欢迎各位读者修正 bug,改进代码和评论。

使用示例代码

本书就是帮助你完成你的工作的。 一般来讲,只要是本书上面的示例代码,你都可以随时拿过去在你的源代码和文档中使用。 除非你使用了大量的代码,否则不需要向我们申请许可。 例如,使用几个代码片段去完成一个程序不需要许可,贩卖或者分发示例代码的光盘则需要许可。 引用本书和示例代码去网上回答一个问题不需要许可,但是合并大量的代码到你的正式产品文档中去则需要许可。

我们不会要求你添加代码的出处,但是如果你这么做了,我们会很感激的。 引用通常包含标题,作者,出版社,ISBN。 例如:Python Cookbook, 3rd edition, by David Beazley and Brian K. Jones (O’Reilly). Copyright 2013 David Beazley and Brian Jones, 978-1-449-34037-7.

如果你觉得你对示例代码的使用超出了合理使用或者上述列出的许可范围, 请随时联系我们,我们的邮箱是 permissions@oreilly.com

联系我们

请将关于本书的评论和问题发送给出版社:

O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)

我们为本书建立了一个网页,其中包含勘误表,示例和一些其他信息。通过可以链接http://oreil.ly/python_cookbook_3e访问。

关于本书的建议和技术性问题,请发送邮件至:bookquestions@oreilly.com

关于我们的书籍,讨论会,新闻的更多信息,请访问我们的网站:http//www.oreilly.com

在Facebook上找到我们:http//facebook.com/oreilly

在Twitter上关注我们:http//twitter.com/oreillymedia

在YouTube上观看我们:http//www.youtube.com/oreillymedia

致谢

我们衷心感谢本书的技术校审人员Jake Vanderplas,Robert Kern和Andrea Crotti非常有用的评论和建议,还有Python社区的帮助和鼓励。我们同样感谢上一个版本的编辑Alex Martelli,Anna Ravenscroft和David Ascher 。尽管这个版本是新创作的,但是是前一个版本为本书提供了一个挑选主题和秘籍的初始框架。最后也是最重要的,我们要感谢所有早期预览版本的读者,感谢你们为本书的改进提出的建议和意见。

1.1 解压序列赋值给多个变量

最后更新于:2017-11-28 14:53:36

问题

现在有一个包含 N 个元素的元组或者是序列,怎样将它里面的值解压后同时赋值给 N 个变量?

解决方案

任何的序列(或者是可迭代对象)可以通过一个简单的赋值语句解压并赋值给多个变量。 唯一的前提就是变量的数量必须跟序列元素的数量是一样的。

代码示例:

>>> p = (4, 5)
>>> x, y = p
>>> x
4
>>> y
5
>>>
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> name, shares, price, date = data
>>> name
'ACME'
>>> date
(2012, 12, 21)
>>> name, shares, price, (year, mon, day) = data
>>> name
'ACME'
>>> year
2012
>>> mon
12
>>> day
21
>>>

如果变量个数和序列元素的个数不匹配,会产生一个异常。

代码示例:

>>> p = (4, 5)
>>> x, y, z = p
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack
>>>

讨论

实际上,这种解压赋值可以用在任何可迭代对象上面,而不仅仅是列表或者元组。 包括字符串,文件对象,迭代器和生成器。

代码示例:

>>> s = 'Hello'
>>> a, b, c, d, e = s
>>> a
'H'
>>> b
'e'
>>> e
'o'
>>>

有时候,你可能只想解压一部分,丢弃其他的值。对于这种情况 Python 并没有提供特殊的语法。 但是你可以使用任意变量名去占位,到时候丢掉这些变量就行了。

代码示例:

>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> _, shares, price, _ = data
>>> shares
50
>>> price
91.1
>>>

你必须保证你选用的那些占位变量名在其他地方没被使用到。

1.2 解压可迭代对象赋值给多个变量

最后更新于:2017-11-28 14:58:25

问题

如果一个可迭代对象的元素个数超过变量个数时,会抛出一个 ValueError 。 那么怎样才能从这个可迭代对象中解压出 N 个元素出来?

解决方案

Python 的星号表达式可以用来解决这个问题。比如,你在学习一门课程,在学期末的时候, 你想统计下家庭作业的平均成绩,但是排除掉第一个和最后一个分数。如果只有四个分数,你可能就直接去简单的手动赋值, 但如果有 24 个呢?这时候星号表达式就派上用场了:

def drop_first_last(grades):
    first, *middle, last = grades
    return avg(middle)

另外一种情况,假设你现在有一些用户的记录列表,每条记录包含一个名字、邮件,接着就是不确定数量的电话号码。 你可以像下面这样分解这些记录:

>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
>>>

值得注意的是上面解压出的 phone_numbers 变量永远都是列表类型,不管解压的电话号码数量是多少(包括 0 个)。 所以,任何使用到 phone_numbers 变量的代码就不需要做多余的类型检查去确认它是否是列表类型了。

星号表达式也能用在列表的开始部分。比如,你有一个公司前 8 个月销售数据的序列, 但是你想看下最近一个月数据和前面 7 个月的平均值的对比。你可以这样做:

*trailing_qtrs, current_qtr = sales_record
trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs)
return avg_comparison(trailing_avg, current_qtr)

下面是在 Python 解释器中执行的结果:

>>> *trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
>>> trailing
[10, 8, 7, 1, 9, 5, 10]
>>> current
3

讨论

扩展的迭代解压语法是专门为解压不确定个数或任意个数元素的可迭代对象而设计的。 通常,这些可迭代对象的元素结构有确定的规则(比如第 1 个元素后面都是电话号码), 星号表达式让开发人员可以很容易的利用这些规则来解压出元素来。 而不是通过一些比较复杂的手段去获取这些关联的元素值。

值得注意的是,星号表达式在迭代元素为可变长元组的序列时是很有用的。 比如,下面是一个带有标签的元组序列:

records = [
    ('foo', 1, 2),
    ('bar', 'hello'),
    ('foo', 3, 4),
]

def do_foo(x, y):
    print('foo', x, y)

def do_bar(s):
    print('bar', s)

for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)

星号解压语法在字符串操作的时候也会很有用,比如字符串的分割。

代码示例:

>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
>>> uname, *fields, homedir, sh = line.split(':')
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
>>>

有时候,你想解压一些元素后丢弃它们,你不能简单就使用 * , 但是你可以使用一个普通的废弃名称,比如 _ 或者 ign (ignore)。

代码示例:

>>> record = ('ACME', 50, 123.45, (12, 18, 2012))
>>> name, *_, (*_, year) = record
>>> name
'ACME'
>>> year
2012
>>>

在很多函数式语言中,星号解压语法跟列表处理有许多相似之处。比如,如果你有一个列表, 你可以很容易的将它分割成前后两部分:

>>> items = [1, 10, 7, 4, 5, 9]
>>> head, *tail = items
>>> head
1
>>> tail
[10, 7, 4, 5, 9]
>>>

如果你够聪明的话,还能用这种分割语法去巧妙的实现递归算法。比如:

>>> def sum(items):
...     head, *tail = items
...     return head + sum(tail) if tail else head
...
>>> sum(items)
36
>>>

然后,由于语言层面的限制,递归并不是 Python 擅长的。 因此,最后那个递归演示仅仅是个好奇的探索罢了,对这个不要太认真了。