<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Coding on 诗与胡说</title>
    <link>https://kylingit.com/categories/coding/index.xml</link>
    <description>Recent content in Coding on 诗与胡说</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh_cn</language>
    <copyright>Copyright © 2021 Kylinking</copyright>
    <atom:link href="https://kylingit.com/categories/coding/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>QQ 空间爬虫之爬取说说</title>
      <link>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E7%88%AC%E5%8F%96%E8%AF%B4%E8%AF%B4/</link>
      <pubDate>Mon, 03 Apr 2017 19:37:30 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E7%88%AC%E5%8F%96%E8%AF%B4%E8%AF%B4/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;今天来讲获取说说~&lt;/p&gt;

&lt;p&gt;为什么把获取说说放在后面讲呢，主要是说说的结构相对来说复杂一点，跟留言不一样，它包括三层结构，一是说说内容本身，二是说说的评论以及回复，第三就是这条说说获得的赞同数，从空间的角度来看可能这三者结合地很好，层次关系和赞同关系一目了然，但是我们从接口获取到的数据并非如此，一层一层来看。&lt;/p&gt;

&lt;h3 id=&#34;接口地址&#34;&gt;接口地址&lt;/h3&gt;

&lt;h4 id=&#34;所有说说&#34;&gt;所有说说&lt;/h4&gt;

&lt;p&gt;首先还是数据的接口地址。方法还是和上篇获取留言一样，从控制台查看请求，从返回的json数据着手处理。
说说的接口地址如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin=目标qq&amp;amp;pos=起始位置&amp;amp;num=10&amp;amp;format=jsonp&amp;amp;g_tk=g_tk值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;url本身就不分析了，和留言的相似，也是需要循环得到&lt;strong&gt;所有说说&lt;/strong&gt;。&lt;/p&gt;

&lt;h4 id=&#34;详细信息&#34;&gt;详细信息&lt;/h4&gt;

&lt;p&gt;之所以强调所有说说，是因为在这里我们可以只取&lt;code&gt;tid&lt;/code&gt;值，这是说说的唯一标识，对于每一条具体的说说还有一个更详细的接口:
&lt;!-- more --&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msgdetail_v6?uin=目标qq&amp;amp;tid=说说id&amp;amp;format=jsonp&amp;amp;g_tk=g_tk值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到这是一个&lt;code&gt;detail&lt;/code&gt;的接口，返回的就是更详细的说说信息，由于太长了就不截图了，可以自己打开看一下。这个地址的关键参数便是刚刚提到的&lt;code&gt;tid&lt;/code&gt;，把&lt;code&gt;tid&lt;/code&gt;传过来，根据这个&lt;code&gt;tid&lt;/code&gt;和发布者，以及登录的&lt;code&gt;g_tk&lt;/code&gt;值，就能得到一条说说包括评论在内的详细信息。&lt;/p&gt;

&lt;h4 id=&#34;点赞情况&#34;&gt;点赞情况&lt;/h4&gt;

&lt;p&gt;上面两个地址能够得到所有说说以及每一条说说的详细内容，但还少了点东西，就是点赞数。上面两个接口的返回数据都没有点赞情况对应的数据，一开始也有点纳闷，这应该是在一个整体里面的，但看了好多遍，确实是没有。后来点了几次&lt;code&gt;xx等x人觉得很赞&lt;/code&gt;，从请求里看到点赞情况是有另外一个接口的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/users.qzone.qq.com/cgi-bin/likes/get_like_list_app?uin=目标qq&amp;amp;unikey=unikey&amp;amp;begin_uin=0&amp;amp;query_count=60&amp;amp;if_first_page=1&amp;amp;g_tk=g_tk值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中&lt;code&gt;unikey&lt;/code&gt;是这条说说的地址：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://user.qzone.qq.com/目标qq/mood/说说id
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Nt7FN&#34; alt=&#34;moods like&#34; /&gt;
需要特别注意的是，这个接口和上面两个不太一样，频繁访问会导致&lt;code&gt;403&lt;/code&gt;，所以若非必要，点赞部分不爬取也是可以的&amp;hellip;或者想一个避免403的方法，比如sleep，可这肯定会使整个程序慢下来，毕竟不敢贸然上多线程&amp;hellip;&lt;/p&gt;

&lt;p&gt;一个小Tips: 点赞数据的获取是不一定要有好友关系的，也就是说可以用小号对赞同数另外爬取，避免被封号&amp;hellip;不是真正的封号，只是无法再访问这个地址了，一般是一天，泪的教训&amp;hellip;&lt;/p&gt;

&lt;p&gt;通过上面第一个地址，可以得到一个好友的所有说说的&lt;code&gt;tid&lt;/code&gt;，根据每个&lt;code&gt;tid&lt;/code&gt;去获取它的详细内容和点赞情况，这样就能把所有说说爬下来了~&lt;/p&gt;

&lt;h3 id=&#34;数据分析&#34;&gt;数据分析&lt;/h3&gt;

&lt;p&gt;前面提到过了，说说和留言不一样，它除了内容本身，还有下面的评论，以及在评论下面的回复，它可能是互动双方，也有可能是第三个好友的回复(留言下面只能是双方的互动)，最后还有点赞的好友的情况&lt;/p&gt;

&lt;p&gt;分析一下返回的数据，这是说说内容和评论内容，图片太长分两次截吧&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/T1o7g&#34; alt=&#34;moods&#34; /&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/vXyMM&#34; alt=&#34;moods&#34; /&gt;&lt;/p&gt;

&lt;p&gt;点赞的情况如下，乱码是因为网站本身的问题&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/s84i0&#34; alt=&#34;moods like&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;数据库设计&#34;&gt;数据库设计&lt;/h3&gt;

&lt;p&gt;刚才提到了，说说的结构相对复杂，考虑到一张表无法将所有字段包括进去，于是设计了三张表，分别是说说内容表，评论及回复表，点赞表&lt;/p&gt;

&lt;h4 id=&#34;说说表-qq-moods&#34;&gt;说说表&lt;code&gt;qq_moods&lt;/code&gt;&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    moodid varchar(30), \			#说说id
    uin varchar(15), \				#发布者
    nickname varchar(50), \			#发布者昵称
    secret int(2), \				#有时候qq签名会同步为说说，设置为仅自己可见时该字段为1
    pubtime varchar(20), \			#发布时间
    phone varchar(30), \			#发布平台
    content TEXT, \				#说说内容，注意TEXT
    pictotal int(4), \				#图片总数
    cmtnum int(4), \				#评论总数
    fwdnum int(4), \				#转发总数
    locate varchar(50), \			#地理位置
    position varchar(50), \
    pos_x varchar(20), \			#经纬度
    pos_y varchar(20))&#39; % tablename
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果如下
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/WEMTS&#34; alt=&#34;moods table&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;评论表-qq-moods-reply&#34;&gt;评论表&lt;code&gt;qq_moods_reply&lt;/code&gt;&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    moodid varchar(30), \			#说说id
    cmtuin varchar(15), \			#评论者
    cmtnickname varchar(80), \			#评论者昵称
    cmtcount varchar(4), \			#该说说评论数
    cmtpubtime varchar(20), \			#评论发布时间
    comtcontent TEXT, \				#评论内容
    replycount varchar(4), \			#该评论下的回复数
    rpypubtime varchar(20), \			#回复发布时间
    replycontent TEXT \				#回复内容
    )&#39; % tablename
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可能有人会问，这怎么表示评论和回复之间的层次关系呢？不急，我们先来看一下效果&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/kmOxu&#34; alt=&#34;moods reply&#34; /&gt;
通过对同一层次的评论和回复插入空值，可以表示出层次关系，这和留言部分是一样的&lt;/p&gt;

&lt;h4 id=&#34;点赞表-qq-moods-like&#34;&gt;点赞表&lt;code&gt;qq_moods_like&lt;/code&gt;&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    moodid varchar(30), \			#说说id
    likecount  varchar(6), \			#点赞总数
    uin varchar(15), \				#点赞者
    nickname varchar(50), \			#昵称
    gender varchar(4), \			#性别
    constellation varchar(10), \		#星座
    addr varchar(10), \				#城市
    if_qq_friend int(2), \			#是否是好友
    if_special_care int(2) \			#是否特别关心
    )&#39; % tablename
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/DkbbK&#34; alt=&#34;moods like&#34; /&gt;&lt;/p&gt;

&lt;p&gt;同样的，同一说说的点赞适当插入空值可以表现出层次关系&lt;/p&gt;

&lt;h3 id=&#34;核心代码&#34;&gt;核心代码&lt;/h3&gt;

&lt;p&gt;考虑到数据解析本身难度并不大，但爬取的逻辑还是挺重要的，所以这里贴一部分关键的代码&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;while True:
    url = &#39;https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin=&#39;+ target_qq + &#39;&amp;amp;pos=&#39; + str(self.moodstatus[&#39;moodPos&#39;]) + &#39;&amp;amp;num=10&amp;amp;format=jsonp&amp;amp;g_tk=&#39; + g_tk
    r = s.get(url, headers = header)
    dict = self.data2json(r.content[10:-2].strip().replace(&#39;\n&#39;,&#39;&#39;))
    if self.moodstatus[&#39;moodPos&#39;] &amp;lt; dict[&#39;usrinfo&#39;][&#39;msgnum&#39;] - 1:				#get 10 items at a time
        self.moodstatus[&#39;moodPos&#39;] += 10
        print &#39;current qq: %s, current pos: %s&#39; % (target_qq, str(self.moodstatus[&#39;moodPos&#39;]))
    else:
   		break

    if dict[&#39;msglist&#39;] == None:
        print u&#39;\n之前动态被封存，无法获取.&#39;
        break

    for item in dict[&#39;msglist&#39;]:
        print &#39;get moodId: %s, moods tid: %s&#39; % (self.moodstatus[&#39;moodId&#39;], item[&#39;tid&#39;])
        url = &#39;https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msgdetail_v6?uin=&#39;+ target_qq + &#39;&amp;amp;tid=&#39;+ item[&#39;tid&#39;] + &#39;&amp;amp;format=jsonp&amp;amp;g_tk=&#39; + g_tk
        r = s.get(url, headers = header)
        data = self.data2json(r.content[10:-2].strip().replace(&#39;\n&#39;,&#39;&#39;).replace(&#39;\\&#39;,&#39;&#39;))

        self.operate_db_moods(db, &#39;qq_moods&#39;, data)							#get moods details
		
        if item.has_key(&#39;commentlist&#39;):
            self.operate_db_moods_reply(db, &#39;qq_moods_reply&#39;, data)			#get moods reply
        self.get_moods_like(qq, target_qq, cookie, item[&#39;tid&#39;], db)			#get moods like
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;解释&#34;&gt;解释&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;moodstatus&lt;/code&gt;是用来存放当前爬虫的状态的，便于程序意外中断后断点续爬。关于如何保存状态，如果有需要的话单独拿出来讲，这里只要关心这个&lt;code&gt;moodstatus&lt;/code&gt;包含的键值对就好了。&lt;code&gt;is_last_mood&lt;/code&gt;用来标识是否爬到了最后一条说说，下次爬取只要检测这个值就能判断是否继续了。
&lt;code&gt;
self.moodstatus = {&amp;quot;moodTid&amp;quot;: &#39;&#39;, &amp;quot;is_last_mood&amp;quot;: 0, &amp;quot;moodPos&amp;quot;: 0, &amp;quot;moodId&amp;quot;: 0, &amp;quot;moodcmtId&amp;quot;: 0, &amp;quot;moodlikeId&amp;quot;: 0}
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data2json(data)&lt;/code&gt;是将&lt;code&gt;request&lt;/code&gt;获取的内容转换为&lt;code&gt;json&lt;/code&gt;对象
&lt;code&gt;
def data2json(self, data):
json_obj = json.loads(data.decode(&#39;utf-8&#39;))
return json_obj
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;operate_db*(self, db, tablename, data)&lt;/code&gt;方法是用来操作数据库的，有建表和插入数据的实现&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;流程图&#34;&gt;流程图&lt;/h4&gt;

&lt;p&gt;画了一个简单的流程图&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/pGVFE&#34; alt=&#34;get moods&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;结束语&#34;&gt;结束语&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;说说部分的代码比其他复杂一些，主要是信息相对较多，数据之间的关系也相对复杂，除了常规边界判断，特殊字符等，还要注意如何正确表示层次关系，以及爬虫状态的保存(谁也不想爬了几千条中断了然后重新开始爬= =)&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;分享一条查询Top 20评论数的sql语句，对于说说表，留言表也是是用的&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SELECT cmtnickname, count(cmtnickname) AS count
FROM qq_moods_reply
WHERE cmtnickname != &#39;&#39;
GROUP BY cmtnickname
ORDER BY count DESC
LIMIT 20
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>QQ 空间爬虫之爬取留言</title>
      <link>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E7%88%AC%E5%8F%96%E7%95%99%E8%A8%80/</link>
      <pubDate>Sat, 01 Apr 2017 19:50:04 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E7%88%AC%E5%8F%96%E7%95%99%E8%A8%80/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;今天来讲爬取所有留言吧~&lt;/p&gt;

&lt;h3 id=&#34;接口分析&#34;&gt;接口分析&lt;/h3&gt;

&lt;p&gt;惯例，从url接口入手&lt;/p&gt;

&lt;p&gt;我们分析一个请求首先要抓到与服务器交互的数据包，这就要用到抓包工具，像&lt;code&gt;Burpsuite&lt;/code&gt;,&lt;code&gt;Fiddler&lt;/code&gt;等，为了方便有时候也直接用&lt;code&gt;chrome&lt;/code&gt;的审查元素&lt;/p&gt;

&lt;p&gt;登录空间，打开&lt;code&gt;f12&lt;/code&gt;，切换到&lt;code&gt;network&lt;/code&gt;选项卡，然后点击留言板，注意下面的请求，找到获取留言的链接
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/xNpNZ&#34; alt=&#34;http request&#34; /&gt;&lt;/p&gt;

&lt;p&gt;它长这样&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/m.qzone.qq.com/cgi-bin/new/get_msgb?uin=登录qq&amp;amp;hostUin=目标qq&amp;amp;start=起始位置&amp;amp;num=一次获取数量&amp;amp;format=jsonp&amp;amp;inCharset=utf-8&amp;amp;outCharset=utf-8&amp;amp;g_tk=g_tk值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;关键参数是：&lt;code&gt;uin&lt;/code&gt;,&lt;code&gt;hostUin&lt;/code&gt;,&lt;code&gt;start&lt;/code&gt;,&lt;code&gt;num&lt;/code&gt;,&lt;code&gt;g_tk&lt;/code&gt;,分别对应&lt;code&gt;登录qq&lt;/code&gt;，&lt;code&gt;目标qq&lt;/code&gt;，&lt;code&gt;起始位置&lt;/code&gt;，&lt;code&gt;一次性获取的留言条数&lt;/code&gt;，&lt;code&gt;g_tk值&lt;/code&gt;，构造出这些参数，就能获取一个好友的所有留言了&lt;/p&gt;

&lt;p&gt;值得注意的是&lt;code&gt;start&lt;/code&gt;和&lt;code&gt;num&lt;/code&gt;，由于该接口的限制，一次最多只能获取20条留言，也就是说&lt;code&gt;num&lt;/code&gt;值最大为20，这就无法一次性获取所有的留言，需要按&lt;code&gt;20条每份&lt;/code&gt;切割开来，每获取20条就让&lt;code&gt;start&lt;/code&gt;增加20，留言总数可以在返回的数据中获取到，然后注意控制边界，我们就能&lt;code&gt;一份一份&lt;/code&gt;获取所有数据了&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&#34;数据分析&#34;&gt;数据分析&lt;/h3&gt;

&lt;p&gt;右键新标签页打开这个链接，分析返回的数据&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/PFpqZ&#34; alt=&#34;http reply&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这也是一串json格式的数据，这样我们就能看到一条留言的存储结构，包括留言总数，留言者，留言内容，留言时间，回复内容等等，所有都get到了，然后设计数据库，存下来就ok了&lt;/p&gt;

&lt;p&gt;可是真的这么顺利吗&amp;hellip;&lt;/p&gt;

&lt;h3 id=&#34;踩坑&#34;&gt;踩坑&lt;/h3&gt;

&lt;h4 id=&#34;坑一&#34;&gt;坑一&lt;/h4&gt;

&lt;p&gt;当一切准备就绪，摩拳擦掌准备大干一场的时候，忽然发现，对方设置了访问权限&amp;hellip;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/IqCvJ&#34; alt=&#34;cannot access&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/ljsdK&#34; alt=&#34;sad&#34; /&gt;&lt;/p&gt;

&lt;p&gt;好吧，再正常不过的事了，谁还没有个小秘密呢(&lt;del&gt;人家根本就不想让你看好嘛&lt;/del&gt;)&lt;/p&gt;

&lt;p&gt;不能看到留言就不能抓到数据了，那如何判断对方是不是不让你访问ta的空间呢？可以看到，当对方设置访问权限的时候，返回的状态码是不一样的，我们可以根据这个状态码&lt;code&gt;code&lt;/code&gt;来判断&lt;/p&gt;

&lt;p&gt;但是再想一下，如果我们要获取说说数据，碰到同样的情况，难道也是来先请求一次留言的接口？也不是不可以，但最好把这两者独立开，避免不同内容混杂在一起。也有可能获取说说的时候又有不一样的状态码，那到时候再判断行不行呢？当然也是可以的&amp;hellip;&lt;/p&gt;

&lt;p&gt;呃&amp;hellip;其实关键的是我们最好找到一个通用的接口，根据这个接口返回的状态做一次判断，这样就能在所有子模块中决定是否对这个好友继续爬取数据，那这个接口是什么呢？&lt;/p&gt;

&lt;p&gt;在上次&lt;a href=&#34;https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B/&#34;&gt;获取好友信息&lt;/a&gt;的那部分中，有一步是根据qq获取&lt;a href=&#34;https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B/#详细信息&#34;&gt;详细信息&lt;/a&gt;，这个详细信息的获取是有好友权限的，不然就可以得到任意qq的信息了&amp;hellip;扯远了，好友权限意味着你必须可以访问ta的空间，这对于设置了访问限制的好友也是一样的，我们同样无法获取到被限制访问的好友的具体信息，于是我们可以再次利用这个接口&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/base.qzone.qq.com/cgi-bin/user/cgi_userinfo_get_all?uin=qq&amp;amp;fupdate=1&amp;amp;outCharset=utf-8&amp;amp;g_tk=g_tk
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当对方限制了我们的访问权限，同样返回一个&lt;code&gt;-4009&lt;/code&gt;状态码，还有&lt;code&gt;您无权访问&lt;/code&gt;的提示信息，这两个都可以用来判断对方是否对我们设置了访问权限&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/pfccO&#34; alt=&#34;cannot access&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;坑二&#34;&gt;坑二&lt;/h4&gt;

&lt;p&gt;好了，现在我们解决了没有访问权限的问题，抛弃了那些早已抛弃我们的小伙伴，再次兴致勃勃地准备大干一场(雾)的时候，忽然发现，私密留言&amp;hellip;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/y81Qe&#34; alt=&#34;secret message&#34; /&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/3I4Aj&#34; alt=&#34;so sad&#34; /&gt;&lt;/p&gt;

&lt;p&gt;好吧，再正常不过的事了，谁还没有好几个小秘密呢(&lt;del&gt;人家双方都不想让你看好嘛&lt;/del&gt;)&lt;/p&gt;

&lt;p&gt;私密留言是看不到具体内容的，一味地取&lt;code&gt;Content&lt;/code&gt;的内容肯定是会出错的，所以还是提前加个判断，判断&lt;code&gt;secret&lt;/code&gt;的值就好了，很简单&lt;/p&gt;

&lt;p&gt;好吧，其实这也不能算是坑了，都是设计过程中要注意的地方，把所有情况都要考虑到。&lt;/p&gt;

&lt;h3 id=&#34;数据库设计&#34;&gt;数据库设计&lt;/h3&gt;

&lt;p&gt;接下来设计数据库，还是为每个好友建立一个独立的表，暂且叫做&lt;code&gt;qq_messages&lt;/code&gt;吧&lt;/p&gt;

&lt;p&gt;留言信息跟好友信息不一样，因为它还有回复，回复也有自己的内容，时间，回复者等信息，所以有一个层次关系，回复的内容嵌在留言内容下面，类似树的结构，所以靠一张表不能很好地表示整个留言关系，于是设计再建一张表，叫做&lt;code&gt;qq_messages_reply&lt;/code&gt;，专门存放一条留言下的回复信息，和留言表有一个key对应，也就是每条留言独有的&lt;code&gt;msgid&lt;/code&gt;，可以认为是外键吧，但这里没有设置成外键，因为感觉不需要&amp;hellip;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;qq_messages&lt;/code&gt;结构&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    msgid varchar(15), \			#留言的唯一标识
    uin varchar(15), \
    nickname varchar(50), \
    secret int(2), \				#私密留言标识
    bmp varchar(20), \
    pubtime varchar(20), \
    modifytime varchar(20), \
    effect char(10), \				#下面三个字段不清楚做什么的，但还是留着吧
    type int(2), \
    capacity varchar(10), \
    ubbContent TEXT, \				#留言内容，注意TEXT
    replyFlag int(2))&#39; % tablename		#是否有回复
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意：由于无法确定留言内容的长度，所以不能确定用多大的存储空间来存储，所以这里将存储结构设置成&lt;code&gt;TEXT&lt;/code&gt;，&lt;code&gt;TEXT&lt;/code&gt;的存储空间是&lt;code&gt;65 535&lt;/code&gt;个字节，大约可以存储&lt;code&gt;20000&lt;/code&gt;个汉字&lt;/p&gt;

&lt;p&gt;关于MySQL可以存储的数据类型以及存储空间，可以参考&lt;a href=&#34;https://www.runoob.com/mysql/mysql-data-types.html&#34;&gt;文档&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可以注意到在&lt;code&gt;qq_messages&lt;/code&gt;中多了一个&lt;code&gt;replyFlag&lt;/code&gt;字段，这个字段是自己加的，用来区分该留言有没有回复，这是根据&lt;code&gt;replyList&lt;/code&gt;是否为空来判断的&lt;/p&gt;

&lt;p&gt;预览&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/cIIY1&#34; alt=&#34;qq messages&#34; /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;qq_messages_reply&lt;/code&gt;结构&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    msgid varchar(15), \
    replycount char(4), \
    uin varchar(15), \
    nickname varchar(50), \
    pubtime varchar(20), \
    content TEXT)&#39; % tablename
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;回复表跟上面差不多，就不解释了&lt;/p&gt;

&lt;p&gt;预览&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/5pPWS&#34; alt=&#34;qq message reply&#34; /&gt;&lt;/p&gt;

&lt;p&gt;插入数据的时候根据回复数插入空值，使看上去有层次关系&lt;/p&gt;

&lt;h3 id=&#34;结束语&#34;&gt;结束语&lt;/h3&gt;

&lt;p&gt;剩下的就是一些小细节了，比如说私密留言获取不到留言者的&lt;code&gt;uin&lt;/code&gt;以及具体的内容，而表的字段已经固定了，无法正确插入怎么办呢？&lt;/p&gt;

&lt;p&gt;留言的内容含有一些特殊字符，比如&lt;code&gt;\&lt;/code&gt;，&lt;code&gt;&#39;&lt;/code&gt;等，让sql语句被转义或被截断，又该怎么办呢？&lt;/p&gt;

&lt;p&gt;还有，留言的回复中又有对话，而且有很多条，这个情况又怎么处理呢？&lt;/p&gt;

&lt;p&gt;╮(╯_╰)╭&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>QQ 空间爬虫之获取好友</title>
      <link>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B/</link>
      <pubDate>Wed, 29 Mar 2017 20:53:43 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;网上有些QQ空间爬虫都是首先设置访问权限为qq好友访问，然后获取所有好友信息。&lt;/p&gt;

&lt;p&gt;其实QQ空间是有接口能够直接获取到所有好友的&lt;/p&gt;

&lt;h3 id=&#34;获取好友&#34;&gt;获取好友&lt;/h3&gt;

&lt;h4 id=&#34;普通信息&#34;&gt;普通信息&lt;/h4&gt;

&lt;h5 id=&#34;接口地址&#34;&gt;接口地址&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?uin=qq&amp;amp;fupdate=1&amp;amp;outCharset=utf-8&amp;amp;g_tk=g_tk
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;g_tk&lt;/code&gt;如何获取&lt;a href=&#34;https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E6%A8%A1%E6%8B%9F%E7%99%BB%E5%BD%95/&#34;&gt;上篇文章&lt;/a&gt;已经提过了&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h5 id=&#34;数据形式&#34;&gt;数据形式&lt;/h5&gt;

&lt;p&gt;请求后返回的是json&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;_Callback(
{
code: 0,
subcode: 0,
message: &amp;quot;&amp;quot;,
default: 0,
data: {
    items: [
        {
            uin: 12345,
            groupid: 2,
            name: &amp;quot;nick0&amp;quot;,
            remark: &amp;quot;remark0&amp;quot;,
            img: &amp;quot;http://qlogo4.store.qq.com/qzone/12345/12345/30&amp;quot;,
            yellow: -1,
            online: 0,
            v6: 1
        },
        {
            uin: 23456,
            groupid: 8,
            name: &amp;quot;nick1&amp;quot;,
            remark: &amp;quot;remark1&amp;quot;,
            img: &amp;quot;http://qlogo3.store.qq.com/qzone/23456/23456/30&amp;quot;,
            yellow: -1,
            online: 0,
            v6: 1
        },
        {
            uin: 34567,
            groupid: 1,
            name: &amp;quot;nick2&amp;quot;,
            remark: &amp;quot;remark2&amp;quot;,
            img: &amp;quot;http://qlogo4.store.qq.com/qzone/34567/34567/30&amp;quot;,
            yellow: -1,
            online: 0,
            v6: 1
        }
    ],
  	gpnames: [
        {
            gpid: 0,
            gpname: &amp;quot;group0&amp;quot;
        },
        {
            gpid: 1,
            gpname: &amp;quot;group1&amp;quot;
        },
        {
            gpid: 2,
            gpname: &amp;quot;group2&amp;quot;
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;关键的是data中的数据，除了所有好友的昵称备注头像外，还有所属的分组id等，本来可以根据这个&lt;code&gt;gpid&lt;/code&gt;进行分组，可是找了一圈没找到如何显示所有分组信息的接口，于是这串数据就没派上用场了&amp;hellip;&lt;/p&gt;

&lt;p&gt;用一个session带上cookie请求这个接口就能获取所有好友了，可以先存下来，方便后面用。&lt;/p&gt;

&lt;h4 id=&#34;详细信息&#34;&gt;详细信息&lt;/h4&gt;

&lt;p&gt;可能有人认为这些信息还是太少了，既然抓取了就索性彻底一些，最好能获取到更详细的信息，于是又经过一番摸索，终于又get到一个接口：&lt;/p&gt;

&lt;h5 id=&#34;接口地址-1&#34;&gt;接口地址&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/base.qzone.qq.com/cgi-bin/user/cgi_userinfo_get_all?uin=qq&amp;amp;fupdate=1&amp;amp;outCharset=utf-8&amp;amp;g_tk=g_tk
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;数据形式-1&#34;&gt;数据形式&lt;/h5&gt;

&lt;p&gt;这是一个&amp;rdquo;详细版&amp;rdquo;的好友信息，包括空间名称，空间描述，出生年月，历史地理位置，现在地理位置等信息，以及更具体的邮箱，手机号等(如果有设置的话)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;_Callback(
{
    code: 0,
    subcode: 0,
    message: &amp;quot;获取成功&amp;quot;,
    default: 0,
    data: {
        uin: 12345
        is_famous: false,
        famous_custom_homepage: false,
        nickname: &amp;quot;nickname&amp;quot;,
        emoji: [ ],
        spacename: &amp;quot;someone&#39;s qzone&amp;quot;,
        desc: &amp;quot;&amp;quot;,
        signature: &amp;quot;this is a signature&amp;quot;,
        avatar: &amp;quot;http://b125.photo.store.qq.com/psb?/blabla&amp;quot;,
        sex_type: 0,
        sex: 1,
        animalsign_type: 0,
        constellation_type: 0,
        constellation: 9,
        age_type: 0,
        age: 18,
        islunar: 0,
        birthday_type: 0,
        birthyear: 1999,
        birthday: &amp;quot;01-01&amp;quot;,
        bloodtype: 0,
        address_type: 0,
        country: &amp;quot;中国&amp;quot;,
        province: &amp;quot;&amp;quot;,
        city: &amp;quot;北京&amp;quot;,
        home_type: 0,
        hco: &amp;quot;中国&amp;quot;,
        hp: &amp;quot;北京&amp;quot;,
        hc: &amp;quot;东城&amp;quot;,
        marriage: 0,
        career: &amp;quot;&amp;quot;,
        company: &amp;quot;&amp;quot;,
        cco: &amp;quot;&amp;quot;,
        cp: &amp;quot;&amp;quot;,
        cc: &amp;quot;&amp;quot;,
        cb: &amp;quot;&amp;quot;,
        mailname: &amp;quot;&amp;quot;,
        mailcellphone: &amp;quot;&amp;quot;,
        mailaddr: &amp;quot;&amp;quot;,
        qzworkexp: [ ],
        qzeduexp: [ ],
        ptimestamp: 1450773545
    }
}
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;嗯&amp;hellip;只要获取到每个好友的qq后接着请求这个接口，更详细的信息就得到了～乖乖存下来&lt;/p&gt;

&lt;h3 id=&#34;数据库设计&#34;&gt;数据库设计&lt;/h3&gt;

&lt;p&gt;对了，本爬虫是基于Python和MySQL的，所以数据都会存在MySQL数据库中，设计为每个好友一个库，含有说说表，说说评论表，说说点赞表，留言表，留言回复表等。
首先好友信息只要获取一遍，存在登录qq的好友表中，字段都是上面获取的数据&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;create_tb_sql = &#39;CREATE TABLE IF NOT EXISTS %s\
    (id int primary key, \
    uin varchar(15), \
    sex int(2), \
    groupid int(2), \
    nickname varchar(40), \
    remark varchar(20), \
    spacename varchar(50), \
    age int(2), \
    birthday varchar(20), \
    city varchar(20), \
    img varchar(60), \
    yellow int(2), \
    online int(2), \
    v6 int(2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4&#39; % tablename		#change mysql encoding to support emoji
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里建表最后的&lt;code&gt;ENGINE=InnoDB DEFAULT CHARSET=utf8mb4&lt;/code&gt;需要解释一下，因为有好多好友的昵称，签名等都是含有emoji表情的，emoji虽然也有编码，但它是用4字节来存储的，而 MySQL 中 utf8 的字段只能存储 1 至 3 字节的字符，所有直接存储会出错，这里就在建表的时候设置表的编码格式为&lt;code&gt;utf8mb4&lt;/code&gt;，该编码是&lt;code&gt;utf8&lt;/code&gt;的超集，向下兼容&lt;code&gt;utf8&lt;/code&gt;，可以参考前阵子写的文章 &lt;a href=&#34;https://kylingit.com/blog/python-%E4%BD%BF%E7%94%A8-mysql-%E5%AD%98%E5%82%A8-emoji-%E8%A1%A8%E6%83%85/&#34;&gt;PYTHON 使用 MYSQL 存储 EMOJI 表情&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;字段含义就不用解释了，注意一下的是&lt;code&gt;birthday&lt;/code&gt;字段是拼接出生年份和具体月日的，就不细分了，&lt;code&gt;city&lt;/code&gt;字段拼接国家省份和城市。&lt;code&gt;sex&lt;/code&gt;字段为&lt;code&gt;0&lt;/code&gt;的表示无法获取该好友的信息&lt;/p&gt;

&lt;p&gt;预览&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/DNYkv&#34; alt=&#34;qq friends&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;结束语&#34;&gt;结束语&lt;/h3&gt;

&lt;p&gt;看似普通的get访问，用request方便又轻松，实际上背后有很多坑&amp;hellip;比如说有些上个年代遗留的火星文&amp;hellip;又比如说各种有意无意在签名中啊说说中啊等带各种&amp;rdquo;特殊字符&amp;rdquo;的，不做过滤直接让程序逼停&amp;hellip;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>QQ 空间爬虫之模拟登录</title>
      <link>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E6%A8%A1%E6%8B%9F%E7%99%BB%E5%BD%95/</link>
      <pubDate>Fri, 24 Mar 2017 14:48:15 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/qq-%E7%A9%BA%E9%97%B4%E7%88%AC%E8%99%AB%E4%B9%8B%E6%A8%A1%E6%8B%9F%E7%99%BB%E5%BD%95/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;想要抓取 QQ 空间数据的第一步就是登录空间，通过好友关系获取说说，日志，留言等。&lt;/p&gt;

&lt;p&gt;话说 QQ 空间登录算法好变态&amp;hellip;4000+ 行 &lt;a href=&#34;https://qzonestyle.gtimg.cn/c/=/qzone/v8/engine/migrate-plugin.js,/qzone/v8/engine/console-plus/console-plus.js,/qzone/v8/engine/request/request_61221.js,/qzone/v8/core/interface_mini.js&#34;&gt;js&lt;/a&gt; 加密，想要读懂该算法也是需要耗费大段时间，好在 github 上有大神实现了该算法，感谢 &lt;a href=&#34;https://github.com/gera2ld&#34;&gt;gera2ld&lt;/a&gt; 大神提供的登录库，为我们省去了大量时间，详情戳 &lt;a href=&#34;https://github.com/gera2ld/qqlib&#34;&gt;qqlib&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于 QQ 空间具体是如何登录的，分析起来比较复杂，关联的 url 也比较多，需要处理的参数更多，如果需要的话会单独拿出来分析，这里跟我们的项目关系不是很大，我们只要能够登录上并且保持登录状态就可以了，所以偷个懒&amp;hellip;&lt;/p&gt;

&lt;p&gt;可以直接用&lt;code&gt;pip&lt;/code&gt;安装&lt;code&gt;qqlib&lt;/code&gt;, 然后&lt;code&gt;import qqlib&lt;/code&gt;使用该库，但由于&lt;code&gt;qqlib&lt;/code&gt;更新频繁，怕到后来有些不兼容，这里选用 2017-03-04 更新的版本，自己加了几个方法的实现。&lt;/p&gt;

&lt;p&gt;本爬虫一个特点就是可以利用上次登录的 cookies 登录，不必每次都通过账号密码登录，当然第一次登录还是要通过账号密码认证，之后从保存的 cookies文件获取内容。cookies 有一定有效期，读取之前会判断该 cookies 是否失效。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h4 id=&#34;1-登录流程&#34;&gt;1. 登录流程&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/vrtFN&#34; alt=&#34;login&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;2-常规登录&#34;&gt;2. 常规登录&lt;/h4&gt;

&lt;p&gt;这段是&lt;code&gt;qqlib&lt;/code&gt;的示例，可以处理含验证码的登录&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def login(self):
	exc = None
	while True:
		try:
			if exc is None:
				self.qq.login()
				break
			else:
				verifier = exc.verifier
				open(&#39;verify.jpg&#39;, &#39;wb&#39;).write(verifier.fetch_image())
				print(&#39;saved verify.jpg&#39;)
				vcode = input(&#39;input verify:&#39;)
				verifier.verify(vcode)
				exc = None
		except qqlib.NeedVerifyCode as e:
			if e.message != None:
				print e.message
			exc = e
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;3-从-cookies-登录&#34;&gt;3. 从 cookies 登录&lt;/h4&gt;

&lt;h5 id=&#34;3-1-保存-cookies&#34;&gt;3.1 保存 cookies&lt;/h5&gt;

&lt;p&gt;登录成功后将 cookies 保存下来，以便下次直接从文件中获取 cookies 用以认证，省去每次从账号密码登录的繁琐，同时也能防止检测到频繁登录(虽然并没有什么用&amp;hellip;)
利用 &lt;code&gt;requests&lt;/code&gt; 库的 &lt;a href=&#34;http://docs.python-requests.org/zh_CN/latest/api.html#requests.utils.dict_from_cookiejar&#34;&gt;dict_from_cookiejar()&lt;/a&gt; 方法可以将 &lt;code&gt;cookiejar&lt;/code&gt; 对象转换为字典，然后利用 &lt;code&gt;pickle&lt;/code&gt; 模块的 &lt;a href=&#34;https://docs.python.org/2/library/pickle.html#pickle.dump&#34;&gt;dump()&lt;/a&gt; 方法将对象存储在文件中&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def save_cookie_to_file(cookie, cookie_file):
	with open(cookie_file, &#39;w&#39;) as f:
		pickle.dump(requests.utils.dict_from_cookiejar(cookie), f)
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;3-2-读取-cookies&#34;&gt;3.2 读取 cookies&lt;/h5&gt;

&lt;p&gt;读取 cookies 方法和保存时一样，只不过把上面的方法反过来执行，利用 &lt;a href=&#34;http://docs.python-requests.org/zh_CN/latest/api.html#requests.utils.cookiejar_from_dict&#34;&gt;cookiejar_from_dict()&lt;/a&gt; 和 &lt;a href=&#34;https://docs.python.org/2/library/pickle.html#pickle.load&#34;&gt;load()&lt;/a&gt; 方法&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def load_cookie_from_file(cookie_file):
	if os.path.isfile(cookie_file):
		with open(cookie_file) as f:
			cookie = requests.utils.cookiejar_from_dict(pickle.load(f))
			return cookie
	return None
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;3-3-cookiejar-对象转字符串&#34;&gt;3.3 &lt;code&gt;cookiejar&lt;/code&gt; 对象转字符串&lt;/h5&gt;

&lt;p&gt;由于 cookies 直接附带在 Headers 中一起发给服务器，所以要将 &lt;code&gt;cookiejar&lt;/code&gt; 对象转成字符串，和其他字段一起组成 Headers&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def cookiejar_to_string(cookies):
	if cookies == None:
		return None
	else:
		cookie = &#39;&#39;
		for keys, values in cookies.iteritems():
			cookie += keys+ &#39;=&#39; + values + &#39;;&#39;
		cookie = cookie[:len(cookie)-1]
		return cookie
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;4-g-tk-值&#34;&gt;4. &lt;code&gt;g_tk&lt;/code&gt; 值&lt;/h4&gt;

&lt;p&gt;不管是直接登录还是从 cookies 登录，非常重要的一点是为了获取 &lt;code&gt;p_skey&lt;/code&gt; 或 &lt;code&gt;skey&lt;/code&gt; 值，这两个值用来计算 &lt;code&gt;g_tk&lt;/code&gt; 值，计算方法已经有代码能够实现了&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def g_tk(self):
	h = 5381
	cookies = self.session.cookies
	s = cookies.get(&#39;p_skey&#39;) or cookies.get(&#39;skey&#39;) or &#39;&#39;
	for c in s:
		h += (h &amp;lt;&amp;lt; 5) + ord(c)
	return h &amp;amp; 0x7fffffff
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;5-检查登录&#34;&gt;5. 检查登录&lt;/h4&gt;

&lt;p&gt;检查是否登录成功思想就是访问该 qq 的用户资料界面，如果能获取成功说明模拟登录成功
该请求是这样子的&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://h5.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/user/cgi_personal_card?uin=用户qq&amp;amp;g_tk=g_tk值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;请求成功返回一段 json，如果 &lt;code&gt;g_tk&lt;/code&gt; 值错误或者请求不合法的话返回错误码 403
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/jQpGH&#34; alt=&#34;user info&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;6-后续&#34;&gt;6. 后续&lt;/h4&gt;

&lt;p&gt;这样我们有了可用的 cookies ，从 cookies 计算&lt;code&gt;g_tk&lt;/code&gt;值，有了&lt;code&gt;g_tk&lt;/code&gt;和好友 qq 号就可以拼接 url 批量获取好友数据了~&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>分享几个有用的小程序</title>
      <link>https://kylingit.com/blog/%E5%88%86%E4%BA%AB%E5%87%A0%E4%B8%AA%E6%9C%89%E7%94%A8%E7%9A%84%E5%B0%8F%E7%A8%8B%E5%BA%8F/</link>
      <pubDate>Sat, 18 Mar 2017 23:21:02 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/%E5%88%86%E4%BA%AB%E5%87%A0%E4%B8%AA%E6%9C%89%E7%94%A8%E7%9A%84%E5%B0%8F%E7%A8%8B%E5%BA%8F/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;1-一個改良的-ping-http-fangpeishi-com-improved-ping-html&#34;&gt;1. &lt;a href=&#34;http://fangpeishi.com/improved_ping.html&#34;&gt;一個改良的 Ping&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;有时候想 ping 一个网址，直接从浏览器复制会带上&lt;code&gt;http://&lt;/code&gt;，粘进命令行就出错了&amp;hellip;
于是可以用这个脚本代替 ping 程序，改成&lt;code&gt;pin&lt;/code&gt;放在&lt;code&gt;/bin&lt;/code&gt;下就 OK 了&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
#author: fangpeishi@gmail.com
#issues:
#  - http(s)://xxx.xx/xxx/xx?xxx
#  - 192.168.1.1/32

new_args=`echo $@ |sed  &#39;s/http.*\:\/\///&#39; |sed &#39;s/\/[^ ]*//&#39;`
#echo ${new_args}
ping -c 4 ${new_args}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&#34;http://fangpeishi.com&#34;&gt;作者&lt;/a&gt;还带上另一个功能，一个网段的也能用
&lt;code&gt;ping 192.168.1.1/32&lt;/code&gt; &amp;mdash;-&amp;gt; &lt;code&gt;ping 192.168.1.1&lt;/code&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/QSTrD&#34; alt=&#34;Untitled Image&#34; /&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&#34;2-随机密码生成脚本&#34;&gt;2. 随机密码生成脚本&lt;/h3&gt;

&lt;p&gt;可以自定义长度&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/bin/bash
L=$2
if [ ! -z $1 ];then
  if [[ &amp;quot;$1&amp;quot; =~ ^[0-9]+$ ]];then
    L=$1
  fi
fi
&amp;lt;/dev/urandom tr -dc &#39;0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&amp;amp;*()_+&#39; | head -c${L}; echo &amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/ooNjB&#34; alt=&#34;Untitled Image&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;3-不定期更新&#34;&gt;3. 不定期更新&lt;/h3&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Github 没有记录 Contributions 的解决</title>
      <link>https://kylingit.com/blog/github-%E6%B2%A1%E6%9C%89%E8%AE%B0%E5%BD%95-contributions-%E7%9A%84%E8%A7%A3%E5%86%B3/</link>
      <pubDate>Fri, 17 Mar 2017 16:46:07 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/github-%E6%B2%A1%E6%9C%89%E8%AE%B0%E5%BD%95-contributions-%E7%9A%84%E8%A7%A3%E5%86%B3/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;最近更新文章的时候发现github没有记录Contributions，也就是小绿墙没有增加小方块，看了下&lt;code&gt;git log&lt;/code&gt;发现是提交的时候用户邮箱写错了，多打了一个字母&amp;hellip;于是Github认为这些commits都不是我提交的 =_=#
网上找到了解决办法&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&#34;1-重新克隆一个repo&#34;&gt;1. 重新克隆一个repo&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;git clone --bare https://github.com/user/repo.git
cd repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;2-新建一个脚本&#34;&gt;2. 新建一个脚本&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;#!/bin/sh
git filter-branch --env-filter &#39;
OLD_EMAIL=&amp;quot;旧的Email地址&amp;quot;
CORRECT_NAME=&amp;quot;正确的用户名&amp;quot;
CORRECT_EMAIL=&amp;quot;正确的Email地址&amp;quot;
if [ &amp;quot;$GIT_COMMITTER_EMAIL&amp;quot; = &amp;quot;$OLD_EMAIL&amp;quot; ]
then
    export GIT_COMMITTER_NAME=&amp;quot;$CORRECT_NAME&amp;quot;
    export GIT_COMMITTER_EMAIL=&amp;quot;$CORRECT_EMAIL&amp;quot;
fi
if [ &amp;quot;$GIT_AUTHOR_EMAIL&amp;quot; = &amp;quot;$OLD_EMAIL&amp;quot; ]
then
    export GIT_AUTHOR_NAME=&amp;quot;$CORRECT_NAME&amp;quot;
    export GIT_AUTHOR_EMAIL=&amp;quot;$CORRECT_EMAIL&amp;quot;
fi
&#39; --tag-name-filter cat -- --branches --tags
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;3-更改旧的邮箱-以及填写正确的用户名和邮箱-执行&#34;&gt;3. 更改旧的邮箱，以及填写正确的用户名和邮箱，执行&lt;/h3&gt;

&lt;h3 id=&#34;4-把正确历史-push-到-github&#34;&gt;4. 把正确历史 push 到 Github&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;git push --force --tags origin &#39;refs/heads/*&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;5-查看-git-log-检查push历史是否被更正-没有错误的话就可以删掉这个clone了&#34;&gt;5. 查看&lt;code&gt;git log&lt;/code&gt; 检查push历史是否被更正，没有错误的话就可以删掉这个clone了&lt;/h3&gt;

&lt;h3 id=&#34;6-done&#34;&gt;6. done&lt;/h3&gt;

&lt;p&gt;参考:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://help.github.com/articles/changing-author-info/&#34;&gt;Changing author info&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://segmentfault.com/a/1190000004318632&#34;&gt;为什么Github没有记录你的Contributions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Python 使用 Mysql 存储 Emoji 表情</title>
      <link>https://kylingit.com/blog/python-%E4%BD%BF%E7%94%A8-mysql-%E5%AD%98%E5%82%A8-emoji-%E8%A1%A8%E6%83%85/</link>
      <pubDate>Mon, 20 Feb 2017 19:43:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/python-%E4%BD%BF%E7%94%A8-mysql-%E5%AD%98%E5%82%A8-emoji-%E8%A1%A8%E6%83%85/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;最近使用 Python 处理数据的时候遇到 mysql 存储 emoji 表情的问题，觉得可以总结一下。&lt;/p&gt;

&lt;h3 id=&#34;一-报错信息&#34;&gt;一. 报错信息&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;Incorrect string value: &#39;\xF0\x9F\x91\x8D&#39; for column &#39;xxx&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;二-错误分析&#34;&gt;二. 错误分析&lt;/h3&gt;

&lt;p&gt;从异常能看出这是编码的问题，当前的配置是数据库连接使用 utf8，字符集也是 utf-8。查阅资料发现，在 mysql 中 utf8 的字段只能存储 1 至 3 字节的字符，而 emoji 表情是使用 4 字节字符来表示的，这就导致 &lt;code&gt;Incorrect string value&lt;/code&gt; 错误。&lt;/p&gt;

&lt;h3 id=&#34;三-解决办法&#34;&gt;三. 解决办法&lt;/h3&gt;

&lt;h4 id=&#34;1-使用-utf8mb4-编码存储数据-utf8mb4-is-a-superset-of-utf8&#34;&gt;1. 使用 &lt;code&gt;utf8mb4&lt;/code&gt; 编码存储数据，&lt;code&gt;utf8mb4 is a superset of utf8&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;utf8mb4 向下兼容 utf8，在 Mysql 5.5.3 以上版本支持 utf8mb4&lt;/p&gt;

&lt;h4 id=&#34;方法-1&#34;&gt;方法(1)&lt;/h4&gt;

&lt;p&gt;修改 mysql 配置. 编辑 my.ini 文件，之后要重启 mysql 服务&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[client]
default-character-set = utf8mb4		# 客户端来源数据的默认字符集

[mysqld]
character-set-server = utf8mb4		# 服务端默认字符集
collation-server = utf8mb4_unicode_ci	# 连接层默认字符集

[mysql]
default-character-set = utf8mb4		# 数据库默认字符集

&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;方法-2&#34;&gt;方法(2)&lt;/h4&gt;

&lt;!-- more  --&gt;

&lt;p&gt;在 python 连接数据库和创建表时指定编码&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import MySQLdb
# 连接
conn = MySQLdb.connect(&amp;quot;127.0.0.1&amp;quot;, &amp;quot;user&amp;quot;, &amp;quot;passwd&amp;quot;)
cursor = self.conn.cursor()
cursor.execute(&amp;quot;SET NAMES utf8mb4&amp;quot;)
cursor.execute(&amp;quot;SET CHARACTER SET utf8mb4&amp;quot;)
cursor.execute(&amp;quot;SET character_set_connection = utf8mb4&amp;quot;)

# 建库
cursor.execute(&#39;CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 \ 
	COLLATE utf8mb4_unicode_ci&#39; % dbname)

# 建表
cursor.execute(&#39;CREATE TABLE table(id int primary key, name char(10))&#39;) \
	ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以查询 mysql 编码方式
&lt;code&gt;show variables like &#39;character_set_%&#39;;&lt;/code&gt;
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/mysql_charset.png&#34; alt=&#34;mysql_charset&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;2-使用正则表达式过滤-emoji-字符&#34;&gt;2. 使用正则表达式过滤 emoji 字符&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python
import re

text = u&#39;This dog \U0001f602&#39;
print(text) # with emoji

emoji_pattern = re.compile(&amp;quot;[&amp;quot;
        u&amp;quot;\U0001F600-\U0001F64F&amp;quot;  # emoticons
        u&amp;quot;\U0001F300-\U0001F5FF&amp;quot;  # symbols &amp;amp; pictographs
        u&amp;quot;\U0001F680-\U0001F6FF&amp;quot;  # transport &amp;amp; map symbols
        u&amp;quot;\U0001F1E0-\U0001F1FF&amp;quot;  # flags (iOS)
                           &amp;quot;]+&amp;quot;, flags=re.UNICODE)
print(emoji_pattern.sub(r&#39;&#39;, text)) # no emoji
&lt;/code&gt;&lt;/pre&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>关于 MHN 的一些部署介绍</title>
      <link>https://kylingit.com/blog/%E5%85%B3%E4%BA%8E-mhn-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%83%A8%E7%BD%B2%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Fri, 30 Sep 2016 14:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/%E5%85%B3%E4%BA%8E-mhn-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%83%A8%E7%BD%B2%E4%BB%8B%E7%BB%8D/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;h3 id=&#34;简介&#34;&gt;简介&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://itandsecuritystuffs.wordpress.com/2015/02/03/honeypot-networks/&#34;&gt;MHN (Modern Honey Network)&lt;/a&gt; 是一个开源软件，它集成了多种蜜罐，简化了蜜罐的部署，同时便于收集和统计蜜罐的数据。
它包括&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sort: &lt;a href=&#34;https://www.snort.org/&#34;&gt;https://www.snort.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Suricata: &lt;a href=&#34;http://suricata-ids.org/&#34;&gt;http://suricata-ids.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Dionaea: &lt;a href=&#34;http://dionaea.carnivore.it/&#34;&gt;http://dionaea.carnivore.it/&lt;/a&gt;, 它是一个低交互式的蜜罐，能够模拟MSSQL, SIP, HTTP, FTP, TFTP等服务 drops中有一篇介绍： &lt;a href=&#34;http://drops.wooyun.org/papers/4584&#34;&gt;http://drops.wooyun.org/papers/4584&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Conpot: &lt;a href=&#34;http://conpot.org/&#34;&gt;http://conpot.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Kippo: &lt;a href=&#34;https://github.com/desaster/kippo&#34;&gt;https://github.com/desaster/kippo&lt;/a&gt;, 它是一个中等交互的蜜罐，能够下载任意文件。 drops中有一篇介绍： &lt;a href=&#34;http://drops.wooyun.org/papers/4578&#34;&gt;http://drops.wooyun.org/papers/4578&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Amun: &lt;a href=&#34;http://amunhoney.sourceforge.net/&#34;&gt;http://amunhoney.sourceforge.net/&lt;/a&gt;, 它是一个低交互式蜜罐，但是已经从2012年之后不在维护了。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Glastopf： &lt;a href=&#34;http://glastopf.org/&#34;&gt;http://glastopf.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Wordpot： &lt;a href=&#34;https://github.com/gbrindisi/wordpot&#34;&gt;https://github.com/gbrindisi/wordpot&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;ShockPot： &lt;a href=&#34;https://github.com/threatstream/shockpot&#34;&gt;https://github.com/threatstream/shockpot&lt;/a&gt;, 模拟的CVE-2014-6271，即破壳漏洞&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;p0f： &lt;a href=&#34;https://github.com/p0f/p0f&#34;&gt;https://github.com/p0f/p0f&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;具体的介绍可以看 WooYun Drops 的一篇文章 &lt;a href=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/others/%E8%9C%9C%E7%BD%90%E7%BD%91%E7%BB%9C.html&#34;&gt;蜜罐网络&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;安装过程不再赘述，讲讲实际部署中遇到的一些问题&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&#34;一些注意点&#34;&gt;一些注意点&lt;/h3&gt;

&lt;h4 id=&#34;一&#34;&gt;一&lt;/h4&gt;

&lt;p&gt;首先，MHN honeymap 是一个控制端，可以认为是“总部”，把它安装在一台机器上就好，它负责记录和显示所有子节点上的攻击数据，部署脚本也在这台机器上，想要部署一个蜜罐到多台服务器上，只要运行它提供的部署脚本就可以，安装和配置都会自动完成，结果会显示在这台控制端。&lt;/p&gt;

&lt;p&gt;先上一个正常运行的页面&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/HoneyMap.jpg&#34; alt=&#34;HoneyMap&#34; /&gt;&lt;/p&gt;

&lt;p&gt;因为部署了差不多100台蜜罐分布在全球不同的地区，这里看上去还是蛮壮观的。红色的代表正在发起攻击的 ip 地理位置，黄色的代表受攻击方，也就是蜜罐的 ip ，显示结果是实时的。&lt;/p&gt;

&lt;p&gt;这是统计信息，包括 TOP 5 IPs, TOP 5 ports, TOP 5 Honey Pots, TOP 5 Sensors, TOP 5 Attacks Signatures，所有记录都会保存在 Mongodb 数据库中，这里只是显示过去 24 小时的&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Dashboard.jpg&#34; alt=&#34;Dashboard&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;二&#34;&gt;二&lt;/h4&gt;

&lt;p&gt;MHN 的 HoneyMap 显示页面使用的是 3000 端口，控制页面运行在 80 端口，80 端口需要验证登录，3000 端口是开放的，建议用 iptables 对端口进行一些保护，需要注意的是，当利用脚本部署不同的蜜罐时，需要从这台服务器下载脚本和配置文件，这时候 80 端口不能被设置成不允许访问，不然没法部署成功。&lt;/p&gt;

&lt;h4 id=&#34;三&#34;&gt;三&lt;/h4&gt;

&lt;p&gt;Map 页面就是显示的地图，这里一开始点击会显示说 404，需要在配置文件把页面地址更改一下
&lt;code&gt;vim /opt/mhn/server/config.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;把
&lt;code&gt;HONEYMAP_URL = &#39;:3000&#39;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;改成
&lt;code&gt;HONEYMAP_URL = &#39;http://ip:3000&#39;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ip 即为该服务器 ip&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/map.jpg&#34; alt=&#34;Map&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Deploy 页面即保存部署蜜罐的脚本，可以下拉选择不同的蜜罐，也可以自定义脚本。它提供的脚本不一定完全适用，有些情况需要自己更改一下。复制上面的 Deploy Command 到另外的机器上执行，完成后一般就会在 Sensors 显示新安装的蜜罐节点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Deploy.jpg&#34; alt=&#34;Deploy&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Attacks 页面显示的是具体的攻击情况，包括时间，节点，国家，源ip和源端口，使用的协议以及发生在哪个蜜罐上&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Attacks.jpg&#34; alt=&#34;Attacks&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Plyloads 页面显示一些攻击行为的 payload，包括 sql 注入，恶意扫描等，它会记录所有 url 访问&lt;/p&gt;

&lt;p&gt;Rules 页面可以自定义一些触发规则，一般保持默认就好&lt;/p&gt;

&lt;p&gt;Sensors 是各个蜜罐节点的情况，成功运行的蜜罐会在这里显示&lt;/p&gt;

&lt;p&gt;Charts 页面会显示 ssh 蜜罐收集到的用户名密码等，这里显示的是 cowrie 捕捉到的一些 ssh 尝试登录，以及 top 用户名和密码&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/passwd.jpg&#34; alt=&#34;Charts&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;关于具体的蜜罐&#34;&gt;关于具体的蜜罐&lt;/h3&gt;

&lt;h4 id=&#34;cowrie&#34;&gt;cowrie&lt;/h4&gt;

&lt;p&gt;这是一个中等交互的SSH蜜罐，关于它的介绍在 Freebuf 上有一篇 &lt;a href=&#34;http://www.freebuf.com/articles/network/112065.html&#34;&gt;文章&lt;/a&gt;，如何安装如何配置在上面都有，不再赘述。
如果运行出错，查看日志发现问题
&amp;gt; getDSAkeys - TypeError: must be long, not mpz&lt;/p&gt;

&lt;p&gt;这是由于 Twisted 库不兼容引起的，解决办法是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cd cowrie/data
$ ssh-keygen -t dsa -b 1024 -f ssh_host_dsa_key
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;需要注意尽量不要将 22 端口暴露在公网，可以使用 iptables 转发到 cowrie 的运行端口，同时将正常的 ssh 服务运行在另外的端口。&lt;/p&gt;

&lt;h4 id=&#34;dionaea&#34;&gt;dionaea&lt;/h4&gt;

&lt;p&gt;这是一个低交互式的蜜罐，能够模拟MSSQL, SIP, HTTP, FTP, TFTP等服务，安装过程参考上面的文章
如果运行
&amp;gt; supervisorctl status&lt;/p&gt;

&lt;p&gt;遇到问题&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;unix:///var/run/supervisor.sock refused connection
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;解决办法是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo supervisord -c /etc/supervisor/supervisord.conf
sudo supervisorctl -c /etc/supervisor/supervisord.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;glastopf&#34;&gt;glastopf&lt;/h4&gt;

&lt;p&gt;glastopf 是一款低交互式 Web 蜜罐，它能记录包括 sql 注入，XSS 攻击等常见的 Web 攻击类型&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/Payloads.jpg&#34; alt=&#34;Payload&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;suricata&#34;&gt;suricata&lt;/h4&gt;

&lt;p&gt;网络入侵检测和阻止引擎
这个蜜罐没有运行成功，后续解决掉问题可能会更新上来。&lt;/p&gt;

&lt;p&gt;目前部署的就这几个蜜罐，其它的可能会根据需要安装部署，有时间的话会更新。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>salt-ssh 配置使用</title>
      <link>https://kylingit.com/blog/salt-ssh-%E9%85%8D%E7%BD%AE%E4%BD%BF%E7%94%A8/</link>
      <pubDate>Thu, 15 Sep 2016 14:54:29 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/salt-ssh-%E9%85%8D%E7%BD%AE%E4%BD%BF%E7%94%A8/</guid>
      <description>&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;salt-ssh 是 Saltstack 框架下的一款批量化远程操作工具，具体介绍可以看 &lt;a href=&#34;http://opensgalaxy.com/2015/08/13/saltstack%E5%85%A5%E9%97%A8%E3%80%90salt-ssh%E3%80%91%E4%BD%BF%E7%94%A8/&#34;&gt;这里&lt;/a&gt;
关于 Saltstack，它是一款自动化运维工具，具体可以浏览 &lt;a href=&#34;https://saltstack.com&#34;&gt;官网&lt;/a&gt;，这里只介绍一下 salt-ssh 的使用。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;salt-ssh 的配置很简单，在 /etc/salt/ 下修改 roster 文件，把需要管理的服务器 ip，用户名，密码按格式配置好即可
&amp;gt; vim /etc/salt/roster&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;server00:
 host: x.x.x.x
 user: root
 passwd: root
 
server01:
 host: x.x.x.x
 user: root
 passwd: root
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后测试一下能不能连通就好了
&amp;gt; salt-ssh &amp;lsquo;*&amp;rsquo; test.ping&lt;/p&gt;

&lt;p&gt;&amp;lsquo;*&amp;rsquo; 是指所有节点，想要单独某个节点的话指定就可以了
&amp;gt; salt-ssh server00 test.ping&lt;/p&gt;

&lt;p&gt;可能需要验证是否接受密钥，不想被提示就加上参数 -i
&amp;gt; salt-ssh &amp;lsquo;*&amp;rsquo; test.ping -i&lt;/p&gt;

&lt;p&gt;测试能够连通就可以执行命令了，使用参数 -r
&amp;gt; salt-ssh &amp;lsquo;*&amp;rsquo; -r &amp;lsquo;uname -a&amp;rsquo; -i&lt;/p&gt;

&lt;p&gt;这里要说的是配置文件里明文记录密码是十分不安全的行为，极端情况是某台服务器被入侵，发现了这个文件，恰巧又有大量服务器配置在这，相当于把机器送到黑客手上了。
即使是加密后的密码也不安全，总之是用文件记录敏感信息都是不负责任的做法。&lt;/p&gt;

&lt;p&gt;想要不在配置文件中记录密码，可以在执行命令的时候把密码作为参数
&amp;gt; salt-ssh &amp;lsquo;*&amp;rsquo; &amp;ndash;passwd &amp;lsquo;password&amp;rsquo; -r &amp;lsquo;args&amp;rsquo; -i&lt;/p&gt;

&lt;p&gt;而配置文件里只要记录 ip 和用户名就可以&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;server00:
 host: x.x.x.x
 user: root
 
server01:
 host: x.x.x.x
 user: root
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样做的优点是不会在文件中泄露密码，缺点是假如每台机器密码不一样，执行起来会比较麻烦，各自取舍吧。 也有通过 keys 验证身份，但测试之后发现还是得认证身份，这里就不提了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tips&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;其实直接在命令中指定密码依然十分危险，因为命令记录会把你出卖&amp;hellip;可以执行一下&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;cat ~/.bash_history&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以涉及到输入密码的命令，可以在输入前键入一个空格，即按一下空格再正常输入命令，这样这条命令就不会被记录在历史里。&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>sqlite 执行删除操作后文件大小不变的解决办法</title>
      <link>https://kylingit.com/blog/sqlite-%E6%89%A7%E8%A1%8C%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C%E5%90%8E%E6%96%87%E4%BB%B6%E5%A4%A7%E5%B0%8F%E4%B8%8D%E5%8F%98%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
      <pubDate>Sat, 27 Aug 2016 13:13:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/sqlite-%E6%89%A7%E8%A1%8C%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C%E5%90%8E%E6%96%87%E4%BB%B6%E5%A4%A7%E5%B0%8F%E4%B8%8D%E5%8F%98%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
      <description>&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;在用python对sqlite3数据库进行删除部分数据的操作后，数据库文件大小并没有改变，上网找了找原因，发现确实是这样 :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When an object (table, index, trigger, or view) is dropped from the database, it leaves behind empty space. This empty space will be reused the next time new information is added to the database. But in the meantime, the database file might be larger than strictly necessary. Also, frequent inserts, updates, and deletes can cause the information in the database to become fragmented - scrattered out all across the database file rather than clustered together in one place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当一个对象（表，索引，触发器或视图）被从数据库中删除，留下一块空白空间。这块空间将被下一次新的信息添加到数据库中重复使用。但在此期间，数据库文件可能变得非常大。此外，频繁的插入，更新和删除可能会导在数据库中的信息成为零散的碎片分布在数据库中，而不是在一个地方聚集在一起。&lt;/p&gt;

&lt;p&gt;解决办法是&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;在数据删除后，手动执行 &amp;ldquo;VACUUM&amp;rdquo; 命令&lt;/li&gt;
&lt;li&gt;在数据库文件创建时，将 auto_vacuum 设置成 &amp;ldquo;1&amp;rdquo; 。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;!-- more --&gt;

&lt;p&gt;但是第二个方法有一定的限制，它只会从数据库文件中截断空闲列表中的页，而不会回收数据库中的碎片，也不会像&lt;code&gt;VACUUM&lt;/code&gt; 命令那样重新整理数据库内容。实际上，由于需要在数据库文件中移动页， &lt;code&gt;auto-vacuum&lt;/code&gt; 会产生更多的碎片。而且，在执行删除操作的时候，会产生一个&lt;code&gt;.db-journal&lt;/code&gt;文件。
使用 &lt;code&gt;auto-vacuum&lt;/code&gt; 的前提是，数据库中需要存储一些额外的信息以记录它所跟踪的每个数据库页都能找回其指针位置。 所以，&lt;code&gt;auto-vacumm&lt;/code&gt; 必须在建表之前就开启。在一个表创建之后， 就不能再开启或关闭 &lt;code&gt;auto-vacumm&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在python中就执行&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import sqlite3
conn = sqlite3.connect(dbfile)
sql = &#39;delete from table where ...&#39;
cu = conn.cursor()
cu.execute(sql)
cu.execute(&#39;vacuum&#39;)
cu.close()
conn.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Python 解析 DNS 时 Resolver instance has no attribute &#39;connectionLost&#39; 异常解决</title>
      <link>https://kylingit.com/blog/python-%E8%A7%A3%E6%9E%90-dns-%E6%97%B6-resolver-instance-has-no-attribute-connectionlost-%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3/</link>
      <pubDate>Fri, 26 Aug 2016 16:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/python-%E8%A7%A3%E6%9E%90-dns-%E6%97%B6-resolver-instance-has-no-attribute-connectionlost-%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3/</guid>
      <description>&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;某个项目中用到dns相关的模块，在长时间运行后偶尔抛出异常:
&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/dns_error.png&#34; alt=&#34;resolver_error&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Resolver instance has no attribute &#39;connectionLost&#39;&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Unhandled Error
Traceback (most recent call last):
  File &amp;quot;dns.py&amp;quot;, line 174, in &amp;lt;module&amp;gt;
    reactor.run()
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/base.py&amp;quot;, line 1169, in run
    self.mainLoop()
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/base.py&amp;quot;, line 1181, in mainLoop
    self.doIteration(t)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/pollreactor.py&amp;quot;, line 167, in doPoll
    log.callWithLogger(selectable, _drdw, selectable, fd, event)
--- &amp;lt;exception caught here&amp;gt; ---
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/python/log.py&amp;quot;, line 84, in callWithLogger
    return callWithContext({&amp;quot;system&amp;quot;: lp}, func, *args, **kw)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/python/log.py&amp;quot;, line 69, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/python/context.py&amp;quot;, line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/python/context.py&amp;quot;, line 81, in callWithContext
    return func(*args,**kw)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/posixbase.py&amp;quot;, line 599, in _doReadOrWrite
    self._disconnectSelectable(selectable, why, inRead)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/posixbase.py&amp;quot;, line 260, in _disconnectSelectable
    selectable.readConnectionLost(f)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py&amp;quot;, line 257, in readConnectionLost
    self.connectionLost(reason)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py&amp;quot;, line 433, in connectionLost
    Connection.connectionLost(self, reason)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py&amp;quot;, line 277, in connectionLost
    protocol.connectionLost(reason)
  File &amp;quot;/usr/lib/python2.7/dist-packages/twisted/names/dns.py&amp;quot;, line 1908, in connectionLost
    self.controller.connectionLost(self)
exceptions.AttributeError: Resolver instance has no attribute &#39;connectionLost&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;!-- more --&gt;

&lt;p&gt;查阅相关资料发现不是自己代码的问题，而是 &lt;code&gt;Twisted&lt;/code&gt; 库中的 &lt;code&gt;twisted.names.client.Resolver&lt;/code&gt; 类没有 &lt;code&gt;connectionLost&lt;/code&gt; 方法，而这个方法本身并不需要做任何事，于是解决办法就是，找到 &lt;code&gt;twisted.names.client.Resolver&lt;/code&gt;，在最后添加 &lt;code&gt;connectionLost&lt;/code&gt; 方法：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def connectionLost(self, p):
    pass
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;异常解决。&lt;/p&gt;

&lt;p&gt;另外，还遇到&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Traceback (most recent call last):
Failure: twisted.names.error.DNSQueryTimeoutError:
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;异常，这个也很奇怪，因为一开始并没有出现，而是运行了一段时间后对某些特定的查询会出现，解决办法是导入dns查询超时异常类，然后捕捉该异常
&lt;code&gt;from twisted.names.error import DNSQueryTimeoutError&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;参考:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://twistedmatrix.com/trac/ticket/5224&#34;&gt;https://twistedmatrix.com/trac/ticket/5224&lt;/a&gt;
&lt;a href=&#34;http://stackoverflow.com/questions/15944617/handle-error-on-a-simple-dns-twisted-client&#34;&gt;http://stackoverflow.com/questions/15944617/handle-error-on-a-simple-dns-twisted-client&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Python2.7 中 UnicodeEncodeError:&#39;ascii&#39; codec can&#39;t encode characters 异常解决</title>
      <link>https://kylingit.com/blog/python2.7-%E4%B8%AD-unicodeencodeerrorascii-codec-cant-encode-characters-%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3/</link>
      <pubDate>Mon, 15 Aug 2016 14:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/python2.7-%E4%B8%AD-unicodeencodeerrorascii-codec-cant-encode-characters-%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3/</guid>
      <description>&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;Python的编码问题一直是一个它的一个缺点，特别是在处理中文上。Python提供了Unicode, str, utf-8, ascii等编码的相互转换，然而还是烦琐易错。
进行sqlite3数据读取并存入文件时碰到了错误 :
&lt;code&gt;UnicodeEncodeError: &#39;ascii&#39; codec can&#39;t encode characters in position 19-22: ordinal not in range(128)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;原因是数据库中含有中文字段，Unicode编码与ASCII编码的不兼容，这个Python脚本文件是由UTF-8编码的，同时Sqlite3数据库存取的也是UTF-8格式，而Python默认环境编码是Ascii:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import sys
&amp;gt;&amp;gt;&amp;gt; print sys.getdefaultencoding()
ascii
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Python调用ascii编码解码程序去处理字符流，当字符流不属于ascii范围内，就会抛出异常&lt;code&gt;ordinal not in range(128)&lt;/code&gt;，解决方法有三种&lt;/p&gt;

&lt;!-- more --&gt;

&lt;ul&gt;
&lt;li&gt;方法一&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更改默认编码&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import sys
reload(sys)
sys.setdefaultencoding(&#39;utf-8&#39;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;把这段代码加在Python文件头部，即可解决异常。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;方法二&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在打开文件时指定编码&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import codecs
fp = codecs.open(&#39;output.txt&#39;, &#39;a&#39;, &#39;utf-8&#39;)
fp.write(data)
fp.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;方法三&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;直接用系统输出byte，不用print&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sys.stdout.buffer.write(data)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;或者&lt;/p&gt;

&lt;p&gt;&lt;code&gt;os.write(sys.stdout.fileno(), data)&lt;/code&gt;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Ganglia 扩展 Python模块</title>
      <link>https://kylingit.com/blog/ganglia-%E6%89%A9%E5%B1%95-python%E6%A8%A1%E5%9D%97/</link>
      <pubDate>Sat, 30 Jul 2016 14:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/ganglia-%E6%89%A9%E5%B1%95-python%E6%A8%A1%E5%9D%97/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;Ganglia支持很多模块的扩展，这里介绍python模块的扩展&lt;/p&gt;

&lt;h3 id=&#34;环境需求&#34;&gt;环境需求&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ganglia 3.1.x&lt;/li&gt;
&lt;li&gt;Python 2.5+&lt;/li&gt;
&lt;li&gt;Python开发头文件&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- more --&gt;

&lt;h3 id=&#34;配置&#34;&gt;配置&lt;/h3&gt;

&lt;p&gt;以下两个目录必须存在，没有则创建
&lt;code&gt;/usr/lib/ganglia/python_modules/&lt;/code&gt;
&lt;code&gt;/etc/ganglia/conf.d/&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&#34;修改配置文件&#34;&gt;修改配置文件&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;vim /etc/ganglia/gmond.conf&lt;/code&gt;
在&lt;code&gt;modules{}&lt;/code&gt;中添加
&lt;code&gt;
modules {
module {
name = &amp;quot;python_module&amp;quot;
path = &amp;quot;/usr/lib/ganglia/modpython.so&amp;quot;
params = &amp;quot;/usr/lib/ganglia/python_modules&amp;quot;
}
module {
#other modules
}
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;添加&lt;code&gt;include(&#39;/etc/ganglia/conf.d/*.pyconf&#39;)&lt;/code&gt;
&lt;code&gt;
include (&#39;/etc/ganglia/conf.d/*.conf&#39;)
include (&#39;/etc/ganglia/conf.d/*.pyconf&#39;)
&lt;/code&gt;
&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;放置-py-及-pyconf&#34;&gt;放置 .py 及 .pyconf&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;/usr/lib/ganglia/python_modules/&lt;/code&gt;下放置python模块代码(.py)
&lt;code&gt;
root@ubuntu:~# ls /usr/lib/ganglia/python_modules/
process_count.py
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;/etc/ganglia/conf.d/&lt;/code&gt;下放置python模块配置文件(.pyconf)
&lt;code&gt;
root@ubuntu:~# ls /etc/ganglia/conf.d/
es_syslog-ng.pyconf
&lt;/code&gt;
重启服务就可以了
&lt;code&gt;service ganglia-monitor restart&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;python模块模板&#34;&gt;python模块模板&lt;/h3&gt;

&lt;p&gt;模块中必须包含以下的三个方法&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def metric_init(params):
def metric_cleanup():
def metric_handler(name):
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前面两个方法的名字必须是一定的，最后一个&lt;code&gt;metric_handler&lt;/code&gt;可以任意命名，具体可以看example&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import random
descriptors = list()
Random_Max = 50
Constant_Value = 50

def Random_Numbers(name):
    &#39;&#39;&#39;Return a random number.&#39;&#39;&#39;
    global Random_Max
    return int(random.uniform(0,Random_Max))

def Constant_Number(name):
    &#39;&#39;&#39;Return a constant number.&#39;&#39;&#39;
    global Constant_Value
    return int(Constant_Value)

def metric_init(params):
    &#39;&#39;&#39;Initialize the random number generator and create the
    metric definition dictionary object for each metric.&#39;&#39;&#39;
    global descriptors
    global Random_Max
    global Constant_Value
    random.seed()

    print &#39;[pyexample] Received the following parameters&#39;
    print params

    if &#39;RandomMax&#39; in params:
        Random_Max = int(params[&#39;RandomMax&#39;])
    if &#39;ConstantValue&#39; in params:
        Constant_Value = int(params[&#39;ConstantValue&#39;])

    d1 = {&#39;name&#39;: &#39;PyRandom_Numbers&#39;,
        &#39;call_back&#39;: Random_Numbers,
        &#39;time_max&#39;: 90,
        &#39;value_type&#39;: &#39;uint&#39;,
        &#39;units&#39;: &#39;N&#39;,
        &#39;slope&#39;: &#39;both&#39;,
        &#39;format&#39;: &#39;%u&#39;,
        &#39;description&#39;: &#39;Example module metric (random numbers)&#39;,
        &#39;groups&#39;: &#39;example,random&#39;}

    d2 = {&#39;name&#39;: &#39;PyConstant_Number&#39;,
        &#39;call_back&#39;: Constant_Number,
        &#39;time_max&#39;: 90,
        &#39;value_type&#39;: &#39;uint&#39;,
        &#39;units&#39;: &#39;N&#39;,
        &#39;slope&#39;: &#39;zero&#39;,
        &#39;format&#39;: &#39;%hu&#39;,
        &#39;description&#39;: &#39;Example module constant (constant number)&#39;}

    descriptors = [d1,d2]
    return descriptors

def metric_cleanup():
    &#39;&#39;&#39;Clean up the metric module.&#39;&#39;&#39;
    pass

#This code is for debugging and unit testing    
if __name__ == &#39;__main__&#39;:
    params = {&#39;RandomMax&#39;: &#39;500&#39;,
        &#39;ConstantValue&#39;: &#39;322&#39;}
    metric_init(params)
    for d in descriptors:
        v = d[&#39;call_back&#39;](d[&#39;name&#39;])
        print &#39;value for %s is %u&#39; % (d[&#39;name&#39;],  v)
&lt;/code&gt;&lt;/pre&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Ubuntu Server 安装配置 Ganglia</title>
      <link>https://kylingit.com/blog/ubuntu-server-%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE-ganglia/</link>
      <pubDate>Fri, 29 Jul 2016 17:54:29 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/ubuntu-server-%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE-ganglia/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;&lt;code&gt;Ganglia&lt;/code&gt;是一个开源的集群监控系统，它基于分层设计，使用广泛的技术，如XML数据代表，便携数据传输，&lt;code&gt;RRDtool&lt;/code&gt;用于数据存储和可视化，可以方便地对机器性能及系统运行状态进行可视化监控，广泛应用于对&lt;code&gt;Hadoop&lt;/code&gt;，&lt;code&gt;Spark&lt;/code&gt;等分布式系统的监测管理。&lt;/p&gt;

&lt;h3 id=&#34;简介&#34;&gt;简介&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ganglia&lt;/code&gt;的核心包含gmond、gmetad以及一个Web接界面。主要是用来监控系统性能，如：cpu 、mem、硬盘利用率， I/O负载、网络流量情况等，通过曲线很容易见到每个节点的工作状态，对合理调整、分配系统资源，提高系统整体性能起到重要作用。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/ganglia.png&#34; alt=&#34;Ganglia&#34; /&gt;&lt;/p&gt;

&lt;h4 id=&#34;gmond&#34;&gt;Gmond&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Ganglia monitoring&lt;/code&gt;，它是一个守护进程，用于收集机器内的metric，它还可以接受别的node发送过来的metric，并且保存一小段时间（几十秒），运行在每一个需要监测的节点上，收集监测统计，发送和接受在同一个组播或单播通道上的统计信息。Gmond可以扮演下面三种角色：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;收集metric并发送出去，同时也接收别的node发送过来的metric；&lt;/li&gt;
&lt;li&gt;只采集metric并发送出去（关键字 deaf）；&lt;/li&gt;
&lt;li&gt;只接收别的机器发送过来的metric（关键字 mute）；
默认情况下，gmond监听8649端口，用来发送和接收udp，tcp数据包。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;gmetad&#34;&gt;Gmetad&lt;/h4&gt;

&lt;p&gt;Ganglia meta daemon，也是一个守护进程，定期检查gmonds ，从那里拉取数据，并将他们的指标存储在RRD存储引擎中。它可以查询多个集群并聚合指标。默认情况下gmond通过multicast的方式发送自己采集到的数据，整个Multicast group里的node都拥有整个cluster的全部metrics。而gmetad可以从一个cluster的任意一个node拿到整个cluster的全部metric并记录到rrd数据库。
默认情况下，gmetad监听8651端口，从这里可以拿到gmetad存放的最新metric数据，也可以给更高层的gmetad使用；监听8652端口，提供数据查询接口，供web使用。&lt;/p&gt;

&lt;h4 id=&#34;rrd&#34;&gt;RRD&lt;/h4&gt;

&lt;p&gt;运行在主节点的一个工具，轮询调度数据库，用于存储数据和可视化时间序列。RRD也被用于生成用户界面的web前端。&lt;/p&gt;

&lt;h3 id=&#34;安装&#34;&gt;安装&lt;/h3&gt;

&lt;h4 id=&#34;安装环境&#34;&gt;安装环境&lt;/h4&gt;

&lt;p&gt;Ubuntu Server 12.04 (10.24.84.23    master node)
Ubuntu Server 16.04 (10.24.84.24    client node)
均需要安装apache服务&lt;/p&gt;

&lt;h4 id=&#34;主节点-master-node&#34;&gt;主节点 master node&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get install ganglia-monitor 
sudo apt-get install rrdtool 
sudo apt-get install gmetad 
sudo apt-get install ganglia-webfrontend
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;子节点-client-node&#34;&gt;子节点 client node&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get install -y ganglia-monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;配置&#34;&gt;配置&lt;/h3&gt;

&lt;h4 id=&#34;主节点-master-node-1&#34;&gt;主节点 master node&lt;/h4&gt;

&lt;h5 id=&#34;修改-gmetad-配置&#34;&gt;修改 gmetad 配置&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;vim /etc/ganglia/gmetad.conf&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# data_source,cluster的名字,可以自定义，但要与gmond配置对应起来
data_source &amp;quot;my cluster&amp;quot; localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;修改-gmetad-配置-1&#34;&gt;修改 gmetad 配置&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;vim /etc/ganglia/gmond.conf&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# 修改 cluster name
cluster {
  name = &amp;quot;my cluster&amp;quot; ## use the name from gmetad.conf
  owner = &amp;quot;unspecified&amp;quot;
  latlong = &amp;quot;unspecified&amp;quot;
  url = &amp;quot;unspecified&amp;quot;
}
# 修改 udp_send_channel，添加 host
udp_send_channel = {
  # mcast_join = xxx.xxx.xxx.xxx
  host = localhost
  port = 8649
  ttl = 1
}
# 修改 udp_recv_channel，注释掉 mcast_join, bind
udp_recv_channel = {
  # mcast_join = xxx.xxx.xxx.xxx
  port = 8649
  # bind = xxx.xxx.xxx.xx
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;重启-ganglia-monitor-和-gmetad-服务&#34;&gt;重启 ganglia-monitor 和 gmetad 服务&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;service gmetad restart
service ganglia-monitor restart
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;子节点-client-node-1&#34;&gt;子节点 client node&lt;/h4&gt;

&lt;h5 id=&#34;修改-gmetad-配置-2&#34;&gt;修改 gmetad 配置&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;# 修改 cluster name
cluster {
  name = &amp;quot;my cluster&amp;quot;
  owner = &amp;quot;unspecified&amp;quot;
  latlong = &amp;quot;unspecified&amp;quot;
  url = &amp;quot;unspecified&amp;quot;
}
# 修改 udp_send_channel host
udp_send_channel {
  # mcast_join = xxx.xxx.xxx.xxx
  host = master node ip
  port = 8649
  ttl = 1
}
udp_recv_channel {
  # mcast_join = xxx.xxx.xxx.xxx
  port = 8649
  # bind = xxx.xxx.xxx.xx
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&#34;重启-ganglia-monitor-服务&#34;&gt;重启 ganglia-monitor 服务&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;service ganglia-monitor restart&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&#34;配置apche2&#34;&gt;配置apche2&lt;/h4&gt;

&lt;h5 id=&#34;复制到apache-www目录&#34;&gt;复制到apache www目录&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;sudo cp -r /usr/share/ganglia-webfrontend /var/www/ganglia&lt;/code&gt;&lt;/p&gt;

&lt;h5 id=&#34;重启apache服务&#34;&gt;重启apache服务&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;sudo /etc/init.d/apache2 restart&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&#34;使用&#34;&gt;使用&lt;/h3&gt;

&lt;p&gt;直接访问&lt;code&gt;http://10.24.84.23/ganglia&lt;/code&gt;就能看到ganglia成功运行&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/images/ganglia_master.png&#34; alt=&#34;ganglia_master&#34; /&gt;&lt;/p&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Ubuntu 16.04 安装 Glastopf</title>
      <link>https://kylingit.com/blog/ubuntu-16.04-%E5%AE%89%E8%A3%85-glastopf/</link>
      <pubDate>Fri, 22 Jul 2016 14:32:18 +0000</pubDate>
      
      <guid>https://kylingit.com/blog/ubuntu-16.04-%E5%AE%89%E8%A3%85-glastopf/</guid>
      <description>

&lt;script src=&#34;https://blog-1252261399.cos-website.ap-beijing.myqcloud.com/pangu.js&#34;&gt;&lt;/script&gt;

&lt;p&gt;&lt;code&gt;glastopf&lt;/code&gt;是一个开源的低交互式Web应用蜜罐，用python编写，可以在各种操作系统上部署，可以方便监控和捕获恶意文件样本，攻击方式等，后期也有开源脚本利于统计分析，同时方便安装配置。&lt;/p&gt;

&lt;h3 id=&#34;项目地址&#34;&gt;项目地址&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mushorg/glastopf&#34;&gt;https://github.com/mushorg/glastopf&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;安装依赖&#34;&gt;安装依赖&lt;/h3&gt;

&lt;p&gt;python要求2.7+，pip要求2.7+&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h4 id=&#34;pymongo&#34;&gt;pymongo&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;pip2.7 install --upgrade pymongo&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&#34;numpy-and-other-deps&#34;&gt;numpy and other deps&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;pip2.7 install numpy
pip2.7 install chardet sqlalchemy lxml beautifulsoup pyOpenSSL requests MySQL-python
pip2.7 install scipy 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(be warned: pip installs software from alpha centauri so expect &lt;em&gt;some&lt;/em&gt; delays. also compiling can take a while.)&lt;/p&gt;

&lt;h4 id=&#34;antlr&#34;&gt;antlr&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;wget http://www.antlr3.org/download/antlr-3.1.3.tar.gz
tar xzf antlr-3.1.3.tar.gz
cd antlr-3.1.3/runtime/Python
python2.7 setup.py install
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;sklearn&#34;&gt;SKLearn&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/scikit-learn/scikit-learn.git
cd scikit-learn
python2.7 setup.py install
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;evnet&#34;&gt;evnet&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/rep/evnet.git
cd evnet
python2.7 setup.py install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;或者直接安装&#34;&gt;或者直接安装&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install python2.7 python-openssl python-gevent libevent-dev python2.7-dev build-essential make
sudo apt-get install python-chardet python-requests python-sqlalchemy python-lxml
sudo apt-get install python-beautifulsoup mongodb python-pip python-dev python-setuptools
sudo apt-get install g++ git php5 php5-dev liblapack-dev gfortran libmysqlclient-dev
sudo apt-get install libxml2-dev libxslt-dev
sudo pip install --upgrade distribute
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;安装php-sandbox&#34;&gt;安装PHP sandbox&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;cd /opt
sudo git clone git://github.com/mushorg/BFR.git
cd BFR
sudo phpize
sudo ./configure --enable-bfr
sudo make &amp;amp;&amp;amp; sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;code&gt;/etc/php/5/cli/php.ini&lt;/code&gt;添加
&lt;code&gt;zend_extension = /usr/lib/php5/20090626+lfs/bfr.so&lt;/code&gt;
或者
&lt;code&gt;zend_extension = /usr/lib64/php/modules/bfr.so&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&#34;安装glastopf&#34;&gt;安装Glastopf&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sudo pip install glastopf&lt;/code&gt;
或者编译安装&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo git clone https://github.com/mushorg/glastopf.git
cd glastopf
sudo python setup.py install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;配置运行&#34;&gt;配置运行&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sudo glastopf-runner&lt;/code&gt;
在目录下会产生db, data, log文件夹和一个glastopf.cfg配置文件，可以配置ip和端口，注意端口不要冲突
db存放本地sqlite数据库文件
运行&lt;code&gt;glastopf-runner&lt;/code&gt;
浏览器访问Web
可以看到如下输出&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2013-03-14 08:34:08,129 (glastopf.glastopf) Initializing Glastopf using &amp;quot;/opt/myhoneypot&amp;quot; as work directory.
2013-03-14 08:34:08,130 (glastopf.glastopf) Connecting to main database with: sqlite:///db/glastopf.db
2013-03-14 08:34:08,152 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connecting to feed broker.
2013-03-14 08:34:08,227 (glastopf.modules.reporting.auxiliary.log_hpfeeds) Connected to hpfeed broker.
2013-03-14 08:34:11,265 (glastopf.glastopf) Glastopf started and privileges dropped.
2013-03-14 08:34:32,853 (glastopf.glastopf) 192.168.10.85 requested GET / on 192.168.10.102
2013-03-14 08:34:32,960 (glastopf.glastopf) 192.168.10.85 requested GET /style.css on 192.168.10.102
2013-03-14 08:34:33,021 (glastopf.glastopf) 192.168.10.85 requested GET /favicon.ico on 192.168.10.102
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;参考&#34;&gt;参考&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mushorg/glastopf/blob/master/docs/source/installation/installation_ubuntu.rst&#34;&gt;https://github.com/mushorg/glastopf/blob/master/docs/source/installation/installation_ubuntu.rst&lt;/a&gt;
&lt;a href=&#34;http://seccentral.blogspot.com/2013/02/how-to-install-glastopf-on-centos-6-in.html&#34;&gt;http://seccentral.blogspot.com/2013/02/how-to-install-glastopf-on-centos-6-in.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;script&gt;pangu.spacingPage();&lt;/script&gt;
</description>
    </item>
    
  </channel>
</rss>