Nov 29, 2016 - 爬虫利器

做过一小段时间的爬虫,大部分网站都是直接下载网页或者找到后端的API接口,连Cookie验证的都很少。一旦有验证,虽然也有对应的套路,但相对来说也麻烦的很。

目前小菜鸟所掌握的无非就是以下的套路:

  1. 在Chrome开发者模式下找到数据来源的API,查看请求中的Cookie或者Token

  2. 在所有请求里,追本溯源找这些Cookie或Token是在哪一步返回的

  3. 模拟请求

通常来说都会先上第三步,有错误再走前两步。其中第三步模拟请求的很简单也很无聊,基本就是配置参数:

requests.get(url, headers, coookie, **kargs)

本文介绍的利器就是解放第三步的无聊工作的。利器组合:Chrome + CurlWget + uncurl

Chrome不必多说。CurlWget是一个扩展,也称”Copy as URLs”,一键模拟curl的参数(curl是终端下的浏览器)。uncurl是一个Python包,将curl的命令参数转换成requests的命令。

一个简单的例子,打开Chrome的开发者模式,点开微博首页。查看Network选项卡:

鼠标这么一点,粘贴到终端,一个回车下去就能看到你微博首页的内容了。用uncurl工具很容易就转成requests的命令。

$ uncurl "拷贝进来curl的命令"
requests.get("http://weibo.com/u/xxxxxxxxx/home?topnav=1&wvr=5",
    headers={
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch",
        "Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6,de;q=0.4",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "DNT": "1",
        "Pragma": "no-cache",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"
    },
   cookies={
        "xxxxxx": "xxxxx",  # 我是不会暴露自己的cookie的...
        "_s_tentry": "login.sina.com.cn",
        "wvr": "6"
    },
)

随意感受一下吧。


2017.2.19: 更新,R语言用户也可以使用curl2r这个小工具把curl的命令转化成R语言的命令。

安装并配置完之后,就可以试试

$ curl2r 拷贝进来curl的命令  # 此处不要用双引号括住!


library(httr)
GET("https://www.baidu.com/",
    add_headers(c(Pragma = "no-cache",
        DNT = "1", `Accept-Encoding` = "gzip, deflate, sdch, br",
        `Accept-Language` = "zh-CN,zh;q=0.8,en;q=0.6,de;q=0.4",
        `Upgrade-Insecure-Requests` = "1",
        `User-Agent` = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
        Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        `Cache-Control` = "no-cache",
        Connection = "keep-alive")),
    set_cookies(XXX))

Jun 15, 2016 - wordcloud2: R语言词云终极解决方案

天上有个魔术师,一会儿黄,一会儿紫,像马像牛又像羊,风儿一吹随风走。

现在你也可以在R语言中绘制多样的词云了。wordcloud2是基于wordcloud2.js封装的一个R包,使用HTML5的canvas绘制。浏览器的可视化具有动态和交互效果,相对于曾经的R包worldcoud,wordcloud2还支持任意形状的词云绘制,这也是我一直想要寻求的特性。

安装&示例

从github安装wordcloud2包:

if (!require(devtools)) install.packages("devtools")
devtools::install_github('lchiffon/wordcloud2')

包内包含了两份data.frame格式的词频数据:英文版的demoFreq和中文版的demoFreqC,使用wordcloud2直接绘制即可。若需要绘制自己的词频数据,只需构造一个类似的数据框即可,第一列代表词语,第二列存贮相应的词频。

```{r cars} library(wordcloud2) wordcloud2(demoFreqC) wordcloud2(demoFreq)

一个粗糙的例子

df = data.frame(letters, rpois(26, 20))

wordcloud2(df)


<img class="img-responsive" src='http://yalei.name/images/demo.jpg'>

### 定制颜色

这里的颜色包括*背景色*和*词语颜色*,分别对应`backgroundColor`和`color`两个参数。这两个参数可以接受CSS认可的任意的参数值,如'red','blue'或者'rgb(0,0,0)'以及十六进制的'#ff2fe9'等。

另外`color`参数有更多可接受的参数:

- 内置的随机色生成方案:`random-light`和`random-dark`,在javasript里定制了相应的随机函数

- javasript回调函数: 用js撰写任意的颜色生成函数,如

```r
js_color_fun = "function (word, weight) {
  return (weight > 2000) ? '#f02222' : '#c09292';
}"
wordcloud2(demoFreqC, color = htmlwidgets::JS(js_color_fun), 
           backgroundColor = 'black')

此处需要使用htmlwidgetsJS函数把字符形式的参数值解析成js函数。

  • R中的函数向量: js回调函数想必让很多纯R语言用户懵逼,最简单的方式是传入一个定制后的颜色向量。wordcloud2(demoFreqC, color = ifelse(demoFreqC[, 2] > 2000, '#f02222', '#c09292')这行代码R语言函数代替了js回调函数,效果相同。

定制形状

学会定义颜色可以让你的词云时黄时紫,却不能像马像牛又像羊。变形最简单的方式就是定义shape参数,如wordcloud2(demoFreqC,shape='star')。还支持’diamond’,’cardioid’等参数(都是在js脚本中预定义好的对应的函数),更多请看函数帮助文档。

内置的几个参数并不十分有趣,完全的自定义才亦可赛艇。wordcloud2允许你传入一张图片,把词云填充在图中的黑色区域。这样,找到一头牛和一匹马,你的云就可以变换了。

wordcloud2(demoFreqC, figPath='~/Desktop/niu.jpg') 
wordcloud2(demoFreqC, figPath='~/Desktop/ma.jpg') 

代码中所需的图片都可以在上图截取,这里就不放了。另外,如果想画成字符形状,你可能需要先画出字符,再使用wordcloud2函数绘制。贴心的作者郎老师早已定制好了相应的接口letterCloud函数,试试运行letterCloud(demoFreqC, word = 'R')

该函数背后的逻辑如上所述,先画出图片再传入figPath参数。到这里,不禁想起cos一篇文章:showtext:字体,好玩的字体和好玩的图形,文中使用showtext包调用神奇的字体画出好玩的图形。此处也可以如法炮制,下载wmpeople1.TTF字体绘制出男人和女人的图案,再调用wordcloud2绘制相应形状的词云。

```{r eval=FALSE} library(showtext) library(Cairo)

link = “http://img.dafont.com/dl/?f=wm_people_1”; download.file(link, “wmpeople1.zip”, mode = “wb”); unzip(“wmpeople1.zip”); font.add(“wmpeople1”, “wmpeople1.TTF”);

plot_shape <- function(filename, char){ CairoPNG(filename, 500, 400) showtext.begin(); plot.new() offset = par(mar = par()$mar) op = par(mar = c(0,0,0,0)) text(0.6, 0.5, char, family=’wmpeople1’, cex=32) par(offset) showtext.end(); dev.off(); }

plot_shape(‘female.png’, ‘u’) plot_shape(‘male.png’, ‘p’)


接下来在淘宝首页搜索'男'和'女'两个关键词。对搜索结果的网页做处理,分词和词频统计。计算TF-IDF指标并用词云展示。绘制词云的数据应当是长尾分布的,即大量低频词和少数高频词。高频词刻画特点,低频词填充剩余位置,达到醒目和美观的效果。因此处理过程中对TF-IDF为0或者缺失的值都填充为1,以便让这些低频词显示出来。

```{r}
library(jiebaR)
readChineseWords <- function (path) {
  # 读取网页或文件 去除标点和英文
  rawstring = readLines(path)
  rawstring = paste0(rawstring, collapse = ' ')
  s = gsub('\\w', '', rawstring, perl=TRUE)
  s = gsub('[[:punct:]]', ' ', s)
  return(s)
}

# 淘宝首页搜索'男'和'女'对应的网页链接
male_link = 'https://s.taobao.com/search?q=%E7%94%B7&search_type=item&sourceId=tb.index'
female_link = 'https://s.taobao.com/search?q=%E5%A5%B3&search_type=item&sourceId=tb.index'

male_str = readChineseWords(male_link)
female_str = readChineseWords(female_link)

# 分词 -> 计算tf-idf 
cc = worker()
new_user_word(cc,'打底裤','ddk')
male_words = cc[male_str]
female_words = cc[female_str]

idf = get_idf(list(male_words, female_words))
get_tf_idf <- function(words){
  words_freq = table(words)
  df = data.frame(name=names(words_freq), freq=as.numeric(words_freq))
  df = merge(df, idf, all.x = TRUE)
  wc_df = data.frame(words=df$name, freq=ceiling(df$count * df$freq * 10))
  # 缺失和0值替换成1 
  wc_df$freq[wc_df$freq == 0 | is.na(wc_df$freq)] = 1
  return(wc_df)
}

# 绘制词云
male_df = get_tf_idf(male_words)
female_df = get_tf_idf(female_words)
wordcloud2(male_df, figPath = 'male.png', 
           backgroundColor = 'black', color = 'random-light')
wordcloud2(female_df, figPath = 'female.png', 
           backgroundColor = 'black', color = 'random-light')

男士的搜索结果主要是“海澜”,“健详”和“牧之逸”的品牌信息(后俩还真没听过…),“棉质”和“衬衣”等,以及臭男人要“防臭”。右中图的“打底裤”,“防走光”和“防晒”,“防水”,主题十分明确,又是一年夏季,女孩的短裙飞扬在街上的每个角落,凉爽的同时也要注意防狼防天气。

另外,为了方便定制特定形状的词云,以下是个方便的shiny app。可以交互的上传图片,选择图中的某一通道(红,绿,蓝,对应RGB)并设置阈值将其二值化,再生成词云查看效果。

shiny::runGist('https://gist.github.com/badbye/8c8d90950ff725a49ac55c6c61164b21')

<img class=”img-responsive”src=’http://yalei.name/images/shiny_wordcloud.png’>

交互

默认生成的词云是自带交互效果的。鼠标悬浮某个词上会显示相应的词频,这是内置的js回调函数效果。可以自编js函数覆盖,比如下边的代码传入一个空函数,画出的词云就没有交互效果了。

hoverFunction = htmlwidgets::JS("function hover() {}")
wordcloud2(demoFreq,hoverFunction = hoverFunction)

这一部分就是纯粹的js了,有能力或有兴趣想要做些不一样效果的,可以瞅瞅源代码中的hover.js

结语

最后,想要更多的细节调试可以参见wordcloud2作者的介绍以及wordcloud2.js的API文档

感谢wordcloud2作者郎大为老师和wordcloud2.js的作者timdream。有兴趣和有能力的朋友可以读读源代码,开开脑洞做些改进,如提升下绘图速度(改进wordcloud2.js的算法),骇客更简单易用的交互接口等。

Jun 29, 2015 - Shell相关概念

Linux下经常听说shell,terminal等之类的概念,最近终于大概明白是什么意思。

shell命令,shell以及终端terminal的关系大致如下:

graph LR; A[Command Line</br>命令&脚本]-->B[Terminal</br>编辑器]; B-->C[Shell</br>解释器]; C-->D[System</br>系统];

由浅入深,在显示器里可以看到的是Terminal终端以及在终端中输入的命令。通过不可见的Shell的解释,在系统中运行,最后将结果返回终端界面。

Commands

Command Line如常用的lscd等命令,也包括比较复杂的管道和重定向等。

Terminal

终端相当于一个编辑器外壳,如Ubuntu里经典的Gnome-terminal。这个网址列出了一些常用的终端,有些支持字体的大小和颜色配置,有些支持分屏,有些支持标签等。终端只是一个外壳,不影响命令的解释和运行。

贴一个我在用的Deepin Terminal,透明背景和分屏功能蛮赞:

<img src=http://yalei.name/images/deepTerminal.png class=”img-responsive”/>

Shell

Shell是任务是对命令进行解释,进而执行。不同的解释器对语法的支持也是不同的,cat /etc/shells查看系统可用的解释器。一般有以下几个:

关于这几个解释器的起源,可以看看介绍。 sh是最早的解释器,现在Linux系统一般都默认使用bash。注意在写Shell脚本时,一般也是调用bash,在首行写#!/bin/bash

最后推荐下Zsh,主要在路径补全代码提示高亮等方面有很大提升,可以看看这个介绍Why Zsh is Cooler than Your Shell

另外,Zsh具有很强的可配置性,常用一个配置是oh-my-zsh

System

最后是系统,一般Mac和类Unix系统都默认搭载有terminal和bash,而Windows用户需要安装Cygwin之类的软件才能模拟运行shell命令。