\chapter{测试封锁情况}
\label{chap:keyword}

\section{关键词搜索测试}

为了调查我国网络审查机制的审查重点，以及它对人们，特别是我们这个年龄的中学生生活的影响，我们决定进行关键词分析。一开始，有很多种备选方案浮现出来：比如制作监视器，供同学安装；设计问卷邀请同学填写等等。

后来，我们发现，其实输入法的词库就是一个非常好的工具 —— 它会记录用户输入的所有词，以及该词被输入过的次数。这应该是最方便，误差最小的一种方式。通过提取用户词库，逐个测试其中的词，即可找到在生活常用词中被屏蔽的那些关键词，同时还可计算出对于生活常用词，我国审查系统封锁的百分比。

调查关键词，要先从提取输入法关键词开始。

\subsection{提取输入法关键词}
\label{subsec:extractwords}
这里以谷歌拼音输入法为例。在输入法的设置页面，可以将谷歌拼音输入法的词库进行导出。导出后的文件使用 GBK 编码，转换编码为 UTF-8，可以看到文件 \texttt{jiehan-utf8.dic} 有 3 栏，使用一个制表符分隔每栏。截取几行如下，用于研究：

\begin{quote}
\begin{verbatim}
啊	666	a
阿	5	a
啊啊	24	a a
啊啊啊	87	a a a
啊啊啊啊	25	a a a a
啊啊啊啊啊	30	a a a a a
啊啊啊啊啊啊	2	a a a a a a
啊啊啊啊啊啊啊	6	a a a a a a a
啊啊啊啊啊啊啊啊啊啊	3	a a a a a a a a a a
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊	1	a a a a a a a a a a a a a a a a
\end{verbatim}
\end{quote}

可以看到数据的格式：第一栏是词语；第二栏是词语被用户输入的次数；第三栏是词语的汉语拼音。

通过简单的浏览，可以看到，这样的词库，显然是不太有用的。因此，我们截取了频数，也就是第二栏的内容，大于等于 30 的所有词来做测试。这样一定程度上避免了无意义词汇被包含在我们的测试范围之内。在 bash 中使用 Awk 代码：
\lstset{language=Bash,basicstyle=\small}
\begin{lstlisting}[breaklines]
jiehan@jiehan-laptop:~$ cat jiehan-utf8.dic | awk '{ if ( $2 > 30 && length( $1 ) >= 2 ) print $1"\t"$2 }' > freq-more-than-30
\end{lstlisting}
来截取出这样符合条件（频数大于等于 30，且长度不小于 2）的词语。再次截取过滤之后的词汇如下：

\begin{quote}
\begin{verbatim}
啊啊啊	87
是啊	68
可爱	35
答案	35
胡岸	46
晚安	263
好吧	81
是吧	43
我把	45
掰掰	35
\end{verbatim}
\end{quote}

\subsection{Google 搜索的自动进行}
\label{subsec:autogoogle}
下一个步骤是进行测试。测试显然需要在中国的网络连接下进行。我们编写了测试程序，来对类似如上文件中的词语或站点 URL 逐个进行测试，然后再将结果写入到专门的文件中，再使用编写的分析程序进行数据统计。

下面提及的代码都可以在\datamedia 中的 \texttt{关键词测试/} 下的 4 个目录中找到，名称分别为 \texttt{test.py} 和 \texttt{analyze.py}。虽然文件名相同，但其内容略有不同：两个输入法词库测试的 \texttt{test.py} 和 \texttt{analyze.py} 内容相同；敏感事件和敏感人物测试的 \texttt{test.py} 和 \texttt{analyze.py} 相同。

\subsubsection{封锁方法人工测试以及测试方法的挑选}
\label{subsubsec:test1}
根据多次人工测试，以及查阅各种资料\footnote{\href{http://zh.wikipedia.org/zh-cn/\%E9\%98\%B2\%E7\%81\%AB\%E9\%95\%BF\%E5\%9F\%8E\#.E5.85.B3.E9.94.AE.E5.AD.97.E8.BF.87.E6.BB.A4.E9.98.BB.E6.96.AD}{维基百科《防火长城》中的``关键字过滤阻断"一节}。}，我们发现，在使用境外搜索引擎搜索的过程中，屏蔽的方法只有重置连接（发送 RST 包）一种。这将非常便于测试 —— 这也是使用境外搜索引擎的原因之一，因为便于判断。而国内搜索引擎（如百度），通常对于敏感词的处理方法是删除搜索结果，有时甚至不对用户进行提示\footnote{来自 \href{http://www.wikileaks.ch/wiki/China:\_censorship\_keywords,\_policies\_and\_blacklists\_for\_leading\_search\_engine\_Baidu,\_2006-2009}{Wikileaks《China: censorship keywords, policies and blacklists for leading search engine Baidu, 2006-2009》}。}。这会给自动化判断带来一定的麻烦。

我们首先测试\texttt{词语样本 1}，它共有 851 个词语信息。为了分次测试方便，我们为测试数据进行了分组，100 个一组，分别以编号（1--9）存入目录 \texttt{关键词测试/输入法样本 1/} 中。

在第~\pageref{subsec:extractwords}~页 \autoref{subsec:extractwords} 提取用户词库的过程中，我们的待测试文件（输入文件）有 2 栏。两栏均取自用户词库。程序在开始运行时会将每行的内容读取并赋值给名为 \texttt{words} 的 list 中。在处理每一行的过程中，程序将把制表符左侧和右侧的内容分开，分别存入 \texttt{word} 和 \texttt{freq} 变量当中，这样我们就得到了关键词及其频数。

之后就会进入 Google 搜索测试的过程。这一过程是通过 Python 3.2 的 \texttt{urllib} 实现的。在编写程序的时候，通过观察用户在浏览器的搜索，找出 Google 搜索的关键词参数为 \texttt{q}。同时，经过测试发现，Google 会判断 \texttt{User-Agent} 字符串，依此只接受一般浏览器的请求。因此，我们截取了 Google Chrome 浏览器的 \texttt{User-Agent} 字串，也在发送请求时一并发给 Google，以伪造一般用户的行为。

\subsubsection{设计测试程序}
经过测试和浏览 \texttt{urllib} 的文档，我们发现在连接被重置后，\texttt{urllib} 会抛出一个 \texttt{urllib.error.URLError} 错误。我们使用 \texttt{try...except} 来处理这样的错误，判断连接是否被重置。

联系 Google 的代码如下：

\lstset{language=Python,caption={关键词测试/输入法样本 1/test.py: test\_by\_google()}}
\begin{quote}
\begin{lstlisting}[breaklines]
def test_by_google(keyword):
	# FIXME 假 User-Agent
	params = urllib.parse.urlencode({'q': keyword})
	headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/11.04'}
	try:
		req = urllib.request.Request("http://www.google.com.hk/search?%s" % params, None, headers)
		result = urllib.request.urlopen(req, None, 5)
		content = result.read().decode('utf-8')
				
		# 检查返回搜索结果中是否包含我们搜索的关键词
		if re.search(keyword, content) == None:
			print("[!] 收到回应, 但 content 中未找到 \"%s\" % keyword")
			return False
		
		print("[~] 无误, content 中找到 \"%s\"" % keyword)
		return True
	except:
		# 发生了异常，代表我们被重置了
		print("[!] \"%s\" 被重置" % keyword)
		return False
\end{lstlisting}
\end{quote}

\label{custom:waitingmechanism}
经过测试和阅读有关文献\footnote{\href{http://zh.wikipedia.org/zh-cn/\%E9\%98\%B2\%E7\%81\%AB\%E9\%95\%BF\%E5\%9F\%8E\#.E5.85.B3.E9.94.AE.E5.AD.97.E8.BF.87.E6.BB.A4.E9.98.BB.E6.96.AD}{维基百科《防火长城》中的``关键字过滤阻断"一节}。}，我们发现，如果被我国网络审查系统重置了连接，那么在约 90 秒内，随后所有的连接都将被重置。因此，我们在程序中设置了一个等待机制。如果一个词被重置，那么程序就会等待一定的时间之后再继续下一个测试。保险起见，我们不仅将延时设置为了 300 秒，同时还使用了一个``安全词"的检测。在 300 秒等待结束后，会搜索一个``安全词"，以测试封锁期是否已经结束。在本例中，这个``安全词"为``\texttt{bhsf}''。程序将会不停等待，直到搜索``安全词"时可以正确地接收到来自 Google 的搜索结果。这部分的代码如下：

\lstset{language=Python,caption={关键词测试/输入法样本 1/test.py: jiehan\_wait() \& wait\_until\_unblock()}}
\begin{quote}
\begin{lstlisting}[breaklines]
def jiehan_wait(interval):
	pbar = ProgressBar(widgets=[Percentage(), Bar()], maxval=300).start()
	for i in range(300):
		time.sleep(interval / 300)
		pbar.update(i+1)
	pbar.finish()

def wait_until_unblock(wait=True):
	sys.stdout.write('[X] 清理: 解封测试:\n')
	
	if wait:
		jiehan_wait(TEST_INTERVAL)
	
	current_wait = 1
	
	sys.stdout.write('[重试 #%d]\n' % current_wait)
	sys.stdout.flush()
	
	while test_by_google(TEST_KEYWORD) == False:
		current_wait = current_wait + 1
		
		jiehan_wait(TEST_INTERVAL)
		sys.stdout.write('[RETRY #%d]\n' % current_wait)
		sys.stdout.flush()
	
	print("[~] 当前解封!\n")
\end{lstlisting}
\end{quote}

\lstset{language=,caption=}

在程序开始时，以及每次测试被重置时，\texttt{jiehan\_wait()} 会被调用。

我们采用一个标记机制来记录程序的测试情况。标记将放在输出文件 \texttt{report-\emph{N}} 中的第三列。标记的含义如下：
\begin{itemize}
	\item \textsf{R} —— 被重置（\textbf{R}eset）：该词被使用重置的方法封锁；
	\item \textsf{P} —— 正常（\textbf{P}ass）：没有发现针对该词的异常。
\end{itemize}

\subsubsection{运行程序}
程序将花费一定时间。运行截图请见第~\pageref{fig:sample1-test}~页上的 \autoref{fig:sample1-test}。

\begin{center}
	\includegraphics[width=1.00\textwidth]{screenshot-sample-1-test.png}
	\captionof{figure}{测试过程中的截图}
	\label{fig:sample1-test}
\end{center}

完成后，输出文件的样式大致如下：
\begin{quote}
\begin{verbatim}
简单	63	P
共产党	39	R
看不到	41	P
找不到	36	P
看到	228	P
\end{verbatim}
\end{quote}

\subsubsection{设计统计程序}
得到了结果，但是数据量非常的大，很难手工统计。因此，我们还设计了一个专用统计程序，统计各词的被屏蔽状况。输出结果有三：测试文件中被封锁的所有词、被封锁词的频数和总频数、被封锁词的个数和词总数。

这里使用了非常简单的 4 个计数器：\texttt{freqSum}、\texttt{freqBlockedSum}、\texttt{wordsSum}，和 \texttt{wordsBlockedSum}。由于过于简单，在此不再赘述。

该部分程序的源代码可以在\datamedia 的 \texttt{关键词测试/} 的各个子目录找到。

程序输出结果形如：
\begin{quote}
\begin{verbatim}
被封关键词:		共产党, 麦当劳, 代理, 王雨萌, 周年, 翻墙, 
封锁频数 (被封/所有):	377/83576
封锁词数 (被封/所有):	6/851
\end{verbatim}
\end{quote}

\subsubsection{引入第二个输入法词库测试样本}
为了保证关键词测试部分的准确性，我们还进行了第二个输入法词库的测试，以尽量减小个人爱好因素造成的偏差。这一个输入法词库取自某一个组员，和上一位组员的爱好相差较大。

\subparagraph{不同点}
由于这组数据的提供者使用谷歌拼音输入法的时间不长，若仍沿用上一次测试所用的提取方法``词频高于 30"，则待测词语就会非常少。因此，我们对于这位同学的词库，提取词频高于 5 的所有二字或以上词语。提取完毕后共计 352 个。对于这 352 个词语，我们的测试方法和第一次测试完全一样。

\subsection{追加的两组关键词测试}
\subsubsection{引入敏感事件和敏感人物词汇表}
\label{subsubsec:test3}
在分析数据的过程中，我们对仅输入法提供的结果不完全满意。因此我们引入了这份由我们自行准备的``敏感事件词汇表''和``敏感人物词汇表''，再次进行测试。更加详细的内容请见第~\pageref{sec:sensitiveanalysis}~页上的 \autoref{sec:sensitiveanalysis} \nameref{sec:sensitiveanalysis}，在此只描述测试方法上的变化。

这次测试，方法大体上相同，只是我们在各个测试和分析程序中去除了词频统计的部分。具体的源代码和测试数据及结果可以在\datamedia 的 \texttt{关键词测试/敏感事件/} 和 \texttt{关键词测试/敏感人物/} 目录中找到。

\section{50 个最热门网站的可访问性测试}
通过互联网使用经验，众所周知的是，除了关键词的封锁，我国还拥有特定网站的访问机制。为了更好地从另一个方面了解封锁情况，我们决定对前 50 个网站进行可访问性测试。

\subsection{从 Alexa 拷贝热门网站域名}
Alexa Internet, Inc. 是一家加利福尼亚州的公司，隶属于 Amazon.com\footnote{根据 \href{http://en.wikipedia.org/wiki/Alexa_Internet}{Wikipedia ``Alexa Internet"页面}。}。它提供较为准确的站点网络流量排名。

在 \url{http://www.alexa.com/topsites} 可以浏览前 500 个热门网站的列表。我们手工将前 50 个域名进行了复制，粘贴并存入到了 \texttt{the\_list} 文件中，这个文件可以在\datamedia 的 \texttt{热门网站访问测试/} 目录中被找到。获取的时间是 2011 年 4 月 29 日，可能和现在的排名情况有出入。

文件的前 5 行形如：
\begin{quote}
\begin{verbatim}
google.com
facebook.com
youtube.com
yahoo.com
blogspot.com
\end{verbatim}
\end{quote}

\subsection{访问测试的自动进行}
\subsubsection{测试方法的设计}
测试站点的可访问性和测试关键词能否搜索，有一些区别。比如，测试关键词能否搜索在算法上的实质是构造 Google 搜索结果页面的 URL，然后检查能否访问，并检查其返回的结果是否是我们期待的；然而，站点的可访问性受多个因素影响，比如站点是否被 DNS 污染（被返回了错误的 IP 地址），以及站点的 IP 地址本身是否已经被屏蔽，导致无法连接到主机。但是，在本次测试中，我们并不关心一个站点到底是被哪种，或者哪几种方法屏蔽。我们关心的是它是否被屏蔽。同时，对于远程站点服务器和我国的屏蔽设备来说，自动访问测试和真实的用户访问是完全一样的，它们并不知道远端的是自动测试程序，还是用户的浏览器。因此，在我们的研究中，自动进行访问测试的原理，大体上与第~\pageref{subsec:autogoogle}~页 \autoref{subsec:autogoogle} 中介绍的，进行 Google 搜索的方法一致。

\subsubsection{设计测试程序}
这部分的程序代码是从进行关键词测试的代码修改而来的。

访问函数将在从文件读取出来的内容前面加上 \texttt{http://}、在后方加上 \texttt{/}，同时删去了测试网站时所不需要的测试步骤。考虑到网站本身的不确定性，同时还将超时时间由 5 秒改为了 10 秒。另外，还去除了第~\pageref{custom:waitingmechanism}~页 \autoref{custom:waitingmechanism}~节中描述的等待机制，因为在这里我们访问的总是不同的主机。

修改之后的访问函数的代码是：
\lstset{language=Python,caption={热门网站访问测试/test.py: try\_access()}}
\begin{quote}
\begin{lstlisting}[breaklines]
def try_access(domain_name):
    testee = 'http://%s/' % domain_name
    headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/11.04'}
    try:
        req = urllib.request.Request(testee, None, headers)
        result = urllib.request.urlopen(req, None, 10)
        
        return True
    except:
        # 发生了异常，该站点无法访问
        print("[!] 无法访问")
        return False
\end{lstlisting}
\end{quote}

在这个情况下，我们还修改了标记机制所使用的标记。在本例中，标记将放在输出文件 \texttt{report-1} 中的第二列。标记的含义如下：
\begin{itemize}
	\item \textsf{F} —— 被重置（\textbf{F}ail）：出于各种原因，该站点无法访问；
	\item \textsf{P} —— 正常（\textbf{P}ass）：没有发现针对该站点首页的异常。
\end{itemize}

这个测试仅针对站点首页，并不能说明站点的每个页面都没有被屏蔽。同时，如果站点在境外，它将依旧受到关键词的检查。

\subsubsection{运行测试}
程序将花费一定时间。完成后，输出文件的样式大致如下：
\begin{quote}
\begin{verbatim}
google.com	P
facebook.com	F
youtube.com	F
yahoo.com	P
blogspot.com	F
\end{verbatim}
\end{quote}

\subsubsection{修改统计程序}
统计程序 \texttt{热门网站访问测试/analyze.py} 也进行了一些小修改，这个文件可以在\datamedia 中找到。

\subsubsection{运行统计程序}
程序输出结果形如：
\begin{quote}
\begin{verbatim}
被封站点:		facebook.com, youtube.com, blogspot.com, 
twitter.com, googleusercontent.com, fc2.com, imdb.com, 
xvideos.com, megaupload.com, 
封锁站点数 (被封/所有):	9/50
\end{verbatim}
\end{quote}

\chapter{分析}
\label{chap:analysis}
\section{生活词汇分析}
\subsection{说明}
这部分内容的数据来自第~\pageref{subsubsec:test1}~页 \autoref{subsubsec:test1} 中描述的第一、二次对于输入法词库的测试。

\begin{description}
	\item[频数] 用户打过的所有词的次数总计。假设，``翻墙"一词打过 10 次，那么它的频数就是 10。频数的比例可以更好的反映实际用户被封锁的可能性。
	\item[封锁词数] 所有词中（不考虑频率）被封锁的个数。
\end{description}

\subsection{结果}
如下两个样本可以在\datamedia 中的 \texttt{关键词测试/} 目录下找到。

\subsubsection{样本 1}
被封关键词：共产党、麦当劳、代理、王雨萌、周年、翻墙。

封锁频数 (被封/所有)：377/83576

封锁词数 (被封/所有)：6/851

\subsubsection{样本 2}
被封关键词：刘本林、周末、吴康妮、周五、复习、学习。

封锁频数 (被封/所有)：65/6767

封锁词数 (被封/所有)：6/352

\subsection{具体分析}
\subsubsection{总体情况}
此次调查被封锁的总频率约为 $0.49\%$；被封锁次数约占词库的 $1.00\%$.

\subsubsection{逐词分析}
\subparagraph{共产党}
{维基百科中``共产党"}、``中国共产党"的链接（内容中立）；中国共产党官方网站；歌颂中国共产党及共产党干部的中国大陆新闻网站。

\subparagraph{麦当劳}
麦当劳官方网站；中外网站对麦当劳的介绍；关于麦当劳的新闻。{其王府井餐厅是近期敏感的``茉莉花革命''在北京的活动地点。}

\subparagraph{代理}
{链接大部分为代理服务器供应商（帮助用户突破网络信息封锁）。}

\subparagraph{王雨萌}
链接均关于名字是``王雨萌''的人。未发现被封锁原因。

\subparagraph{周年}
链接为中华人民共和国建国、中国共产党成立、五四运动、武昌起义、汶川地震以及各种学校、博物馆等机构的周年纪念。{只有一个``文化大革命''40 周年是敏感内容}。

\subparagraph{翻墙}
{链接大部分为代理服务器供应商（帮助用户突破网络信息封锁）以及对突破网络封锁的介绍。}

\subparagraph{刘本林}
链接均关于名字是``刘本林''的人。其中有一个{犯罪分子}。{``刘本昕''（相近的名字）是一个饱受争议的政治敏感人物}。

\subparagraph{周末}
{链接有《南方周末》的网页。}

\subparagraph{吴康妮}
链接均关于名字是``吴康妮''的人，无敏感人物。{``吴康轩''（相近的名字）为有争议的政治人物。}

\subparagraph{周五}
未发现被封锁原因。

\subparagraph{复习}
链接均为教育网站的中考、高考复习网页。未发现被封锁原因。

\subparagraph{学习}
链接均为各种考试备考网页。未发现被封锁原因。\\

通过对以上被封锁词的分析，我们对中国互联网审查封锁关键词类型做出以下猜测：

\begin{enumerate}
	\item 政治敏感词。其中包括：政治敏感事件、日期、机构、人物。
	\item 与敏感词相近的词。
	\item 按一定比例封锁输入的词。
\end{enumerate}

\subsection{总结}
测试时，输入法输入的内容很多，而很多生活上的词语是肯定不会拿去搜索引擎搜索的，所以导致了被封锁的词比我们预期要低。这次测试是对生活中常用的词语的抽样调查。

\section{追加调查的分析}
\label{sec:sensitiveanalysis}

\subsection{说明}
第一轮调查，我们运用的词库中有很多口语词，敏感词较少。追加调查中，我们测试了草拟的政治敏感词与政治敏感人物表，并进行测试。测试搜索引擎：Google。

这部分内容的数据来自第~\pageref{subsubsec:test3}~页 \autoref{subsubsec:test3} 中描述的第三、四次对于输入法词库的测试。

\subsection{结果}
\subsubsection{敏感事件词汇表样本}
被封关键词：六四、天安门事件、诺贝尔和平奖、共产党、中国共产党、零八宪章、天安门母亲、法轮功、九评共产党、退党、三退、退党保平安、大纪元、茉莉花、六四戒严、叛徒内奸工贼刘少奇、China's Human Rights Record。

封锁词数 (被封/所有)：17/42

\subsubsection{敏感人物词汇表样本}
被封关键词：方励之、李锐、胡继伟、王若水、吴国光、胡平、刘晓波、达赖喇嘛、赵紫阳、鲍彤、高智晟、胡佳、姜力钧、李海、李智、刘荻、谭作人、王丹、王军涛、王小宁、王炳章、王有才、魏京生。

封锁词数 (被封/所有)：23/49

\subsection{具体分析}
封锁词数占输入词数的一部分。还有一部分敏感词没有被封锁。

\subsection{人工追加测试}
\paragraph{吾尔开希}
未被封锁，但是搜索结果的所有链接几乎都被封锁。

\paragraph{苏晓康}
未被封锁，但是前两页搜索结果只有 \url{http://wjiausa.blog.hexun.com/38275291_d.html} 和 \url{http://article.netor.com/m/jours/adindex.asp?boardid=27420&joursid=42906} 的链接可以打开。根据其内容，我们怀疑这是漏封的网页。

\subsection{总结}
有一些词可以并不在搜索中被封锁，而在网页中直接被封锁。这说明，有一些词在即使在搜索引擎中不被封锁，但是会在其他网页中被封锁。一个原因是，也许封网部门漏封了一个关键词，所以搜索引擎得以显示，但是因为网页中此敏感词与其他被封锁敏感词同时出现，所以网页会被封锁。
