asuerhao's Blog

如果有什么做的不到的地方请尽管留言, 我会改进的 : )

使用perl的“WWW::Mechanize”模块编写操作网页的自动化脚本

声明:这篇文章刚刚在飞测门户发过,作者都是我。
   Perl的“WWW::Mechanize”模块可以实现对网页的多种操作,比如查找页面内的链接、内容、页面信息、填写表单、submit等功能,适合 用于网站的功能行测试,下面提供一个示例脚本:使用微软的必应引擎搜索"love"关键词,并在结果页面查找是否存在某一特定链接,在终端打印相应提示。 为了更完善地说明“WWW::Mechanize”模块的功能,脚本写得很罗嗦:

#! /usr/bin/perl
# Author: ycjiang
use 5.010;
use warnings;
use diagnostics;

use WWW::Mechanize;
# use URI;
# use HTTP::Response;
# use WWW::Mechanize::Link;

use utf8;
binmode(STDIN, ':encoding(utf8)');
binmode(STDOUT, ':encoding(utf8)');
binmode(STDERR, ':encoding(utf8)');

my $mech = WWW::Mechanize->new();
my $uri = 'http://www.cn.bing.com';
my $uri_hostname = 'cn\.bing\.com';
my $alias = 'Linux Mozilla';
my $agent = ${mech}->agent_alias($alias);
my @links;
my @links_samehost;
my @forms;
my $title = '';

# 确定客户端标识
$agent = ${mech}->agent_alias($alias);
say("所支持的客户端标识:");
foreach (${mech}->known_agent_aliases()) {
    say($_);
}
say("当前客户端标识:".$agent);
say("");

# 打开页面
eval {
    $mech->get($uri);
};
say("当前处理的页面:".(${mech}->uri->as_string));
# say("正在刷新当前页面......");
# $mech->reload();    # 调用reload方法不会修改浏览历史
if (! ${mech}->success) {
    die("Oops! 打开页面 \"".(${mech}->uri->as_string)."\" 失败: ".(${mech}->res->status_line));
} else {
    say("成功打开页面 \"".(${mech}->uri->as_string)."\" 状态码: ".(${mech}->status));
}
say("");

# 网页头
$title = ${mech}->title;
say("网页头: ", $title);
say("");

# 网页中的链接
@links = ${mech}->links;
say("页面中存在的所有链接:");
foreach (@links) {
    if(${_}->text) {
        print("\"", ${_}->text, "\" 的URL为: ");
    } else {
        print("\"undefined\" 的URL为: ");
    }
    if(${_}->url =~ m/^\//) {
        say(${mech}->uri->as_string.${_}->url);
    } elsif (${_}->url =~ m/^\?/) {
        say(${mech}->uri->as_string.'/'.${_}->url);
    } else {
        say(${_}->url);
    }
}
say("");

# 页面上的可显示文本:
say("页面上的可显示文本:");
say(${mech}->content(format => 'text'));
say("");

# 查看是否存在某一链接:
say("页面上可以找到的第五个链接为: ".$mech->find_link(n => 5)->url);
say("页面上可以找到的与本页面在同一域名的链接为: ");
foreach (@links) {
    if(${_}->url =~ m/(^\/)|(^\?)|(^http.{3,4}(www){0,1}${uri_hostname})/) {
        print(${_}->url);
        push(@links_samehost, ${_}->url);
        if(${_}->text) {
            say(": ",${_}->text);
        } else {
            say(": undefined");
        }
    }
}
say("");

# 表单操作:
# @forms=$mech->forms;
say("表单操作: ");
$mech->form_id('sb_form');
$mech->field('q', 'love');
say("在搜索框内填写了单词\"love\"");
$mech->submit();
say("点击搜索...");
say("现在正在处理的网页地址为: ");
say(${mech}->uri->as_string);
# say("此时页面上的可显示文本为:");
# say(${mech}->content(format => 'text'));
say("在页面上查找指定的链接(http://baike.baidu.com/view/4409.htm, 百度百科的love词条),查找结果为:");
if($mech->find_link(url_regex=>qr/baike\.baidu\.com\/view\/4409\.htm/)){
    say("找到了");
} else {
    say("未找到");
}
say("");

LFS-7.1安装笔记, 'devtmpfs filesystem问题'

    系统安装完成, 重启, 可是内核挂载虚拟文件系统‘/dev’时提示错误: 
’error unknown filesystem type devtmpfs‘.

    只好再次重启, 回到寄主系统中排查错误。看到文件/etc/fstab中有一行:
devtmpfs /dev devtmpfs mode=0755,nosuid 0 0
    内核在挂载这一文件系统时出错, google后才知道, 配置内核时还需要手动选择'devtmpfs filesystem'的支持:
Device Drivers --->
  Generic Driver Options --->
     Maintain a devtmpfs filesystem to mount at / dev
    重新编译内核, 重启, 问题解决.

    这似乎是在LFS-7.1中加入的新特性,不过手册中没有说清楚。
原帖地址:https://answers.launchpad.net/lfscript/+question/186427

汉诺塔的递归实现

    汉诺塔问题简介: http://zh.wikipedia.org/wiki/%E6%B1%89%E8%AF%BA%E5%A1%94

    其递归实现的函数原型为: 'void hanoi(int n, char a, char b, char c);'.  其中字符变量'a', 'b', 'c'分别表示从杆子a, 杆子b, 杆子c, 整数变量'n'表示汉诺塔问题的阶数(即杆子a上有n个待移动到杆子c的圆盘, 杆子b作为临时杆子).

    其递归实现的步骤为:
  1. 如果n==1, 则直接将唯一的圆盘从杆子a移动到杆子c, 问题解决.
  2. 否则, 将杆子a上的(n-1)个圆盘移动到杆子b(通过递归实现),
       然后将杆子a上剩余的最大的圆盘移动到杆子c,
       最后将杆子b上的(n-1)个圆盘移动到杆子c(通过递归实现), 问题解决.

其递归实现的C代码为:

#include<stdio.h>
void hanoi(int, char, char, char);
int main(void)
{
	int n=3;
	printf("输入数字n:\n");
	scanf("%d", &n);
	hanoi(n, 'A', 'B', 'C');
	return 0;
}
void hanoi(int n, char a, char b, char c)
{
	if(n==1) {
		printf("%c->%c\n", a, c);
	} else {
		hanoi(n-1, a, c, b);
		printf("%c->%c\n", a, c);
		hanoi(n-1, b, a, c);
	}

}

  之前在笔试中遇到的, 记录一下.

C 数据类型转换

    看一下段代码, 分析for循环的循环体会执行多少次:

#include<stdio.h>
int main(void)
{
	int i;
	for(i=-1; i < sizeof(int); i++) {
		printf("%d, ", i);
	}
	return 0;
}

    实验表明, 在不同的编译器下, 这个循环的实现也不一样:

    1. 在gcc下, 由于'sizeof(int)'的返回值是'unsigned long int'类型的, 但'i'的类型是'signed int'类型的, 二者在做比较时要将i从'signed int'转换成'unsigned long int', 但由于i原来是有符号的'-1', 转换后变为无符号的'0xffffffff', 当然比'sizeof(int)'大, 结果for循环体一次也没有执行.

    2. 在Turbo C下, 由于'sizeof(int)'的返回值是'signed int'类型的(注意: 这不符合C标准的规定), 所以与'i'比较是没有类型转换, for循环体会执行5次.

    严重注意: 有些东西了解就行, 以便在出现问题时知道如何解决, 但尽量不要在自己的代码中尝试那些东西.

C语言 char型也是分有符号数和无符号数的

  unsigned char型表示无符号数, 取值范围是 0~255.
  signed char型表示无符号数, 取值范围是 -128~127.

  在x86平台上, gcc规定不带'unsigned'或'signed'关键字的char是有符号的. 参见以下代码 :

#include<stdio.h>
int main(void)
{
	printf("(signed char)200: %d\n", (signed char)200);
	printf("(unsigned char)200: %d\n", (unsigned char)200);
	printf("(char)200: %d\n", (char)200);	// gcc定义char型是有符号的。
	return 0;
}


打印结果为:

(signed char)200: -56
(unsigned char)200: 200
(char)200: -56

  利用objdump工具对其进行反汇编, 观察向printf函数传递的第二个参数, 节选部分汇编代码:
    printf("(signed char)200: %d\n", (signed char)200);
 80483cd:    b8 e0 84 04 08                mov    $0x80484e0,%eax
 80483d2:    c7 44 24 04 c8 ff ff        movl   $0xffffffc8,0x4(%esp)
 80483d9:    ff
 80483da:    89 04 24                            mov    %eax,(%esp)
 80483dd:    e8 12 ff ff ff                    call   80482f4 <printf@plt>
    printf("(unsigned char)200: %d\n", (unsigned char)200);
 80483e2:    b8 f6 84 04 08                mov    $0x80484f6,%eax
 80483e7:    c7 44 24 04 c8 00 00    movl   $0xc8,0x4(%esp)
 80483ee:    00
 80483ef:    89 04 24                            mov    %eax,(%esp)
 80483f2:    e8 fd fe ff ff                     call   80482f4 <printf@plt>
    printf("(char)200: %d\n", (char)200);    // gcc定义char型是有符号的。
 80483f7:    b8 0e 85 04 08                mov    $0x804850e,%eax
 80483fc:    c7 44 24 04 c8 ff ff         movl   $0xffffffc8,0x4(%esp)
 8048403:    ff
 8048404:    89 04 24                            mov    %eax,(%esp)
 8048407:    e8 e8 fe ff ff                    call   80482f4 <printf@plt>

  还有一个问题需要注意, 从汇编代码可以看到, 实际上用了四个字节存储char类型的参量, 即: 像printf这种形参类型未知的函数, 调用函数时编译器会自动对相应的实参做Integer Promotion.