imgcat简介

Mac下终端软件 iterm2 有个工具:imgcat。
可以在iterm2终端下展示图片。
地址是: https://www.iterm2.com/utilities/imgcat
安装方式就不赘述了。

核心代码

通过简化代码得到

1
2
3
4
5
6
7
8
9
#!/bin/bash

img=$(base64 < "$1")

printf "\033]1337;File=;inline=1:"
printf "$img"
printf "\a"

exit 0

嗯 很简单的操作,仅在iterm2下生效。

至于原理:
参见Wiki百科:https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97

PS:
本来打算研究下的,看完Wiki后果断放弃

啥是S3

Amazon S3,全名Amazon Simple Storage Service。
S3的初衷很简单,”malloc internet“。
简单来说就是Amazon提供的网络数据存储服务。
详细了解可参见官方文档:https://aws.amazon.com/cn/s3/

域名如何进化的

官方声明:2020年9月30日或之前创建的bucket将继续支持Path Style模型。必须使用Virtual Hosted Style引用该日期之后创建的bucket。

Path Style

1
2
3
https://s3.Region.amazonaws.com/bucket-name/key name
# 例如
https://s3.us-west-2.amazonaws.com/mybucket/puppy.jpg

Virtual Hosted Style

1
2
3
https://bucket-name.s3.Region.amazonaws.com/key name
#例如
https://my-bucket.s3.us-west-2.amazonaws.com/puppy.png

为啥要改?

  • 分压

    Path Style 导致所有S3用户访问同一域名 s3.Region.amazonaws.com。随着用户增长,DNS解析,扩展,安全性和流量管理(包括DDoS保护)都会越来越具有挑战性,或者说困难。

  • 某些新的特性

    比如弃用一些旧版本的安全配置将会更加容易

为啥要慢慢改?

  • 某些bucket名称中有”.”,导致无法使用 Virtual Hosted Style。
  • URL路径中的某些字符在域名中无效。例如域名不区分大小写,但路径区分

参考:

前言

一个合格的开发者,应该要熟练地使用Google、Baidu等搜索引擎(不然怎么复制别人的代码)。而搜索引擎为了方便用户的使用,基本都提供了高级搜索功能。而今天介绍的Github,作为全球最大的同性交友网站,存储的代码量可能是全世界最多的。为了方便大家寻找自己需要的代码,同样提供了很多方便的高级搜索语法。


搜索简介

先上文档:https://help.github.com/en/github/searching-for-information-on-github。
此文档为Github官网介绍搜索语法的文档,提供各种语言版本,比如中文。

搜索入口

基本每个github页面左上角都有个搜索框,可直接输入关键字进行搜索。或者也有专门的搜索页面:https://github.com/search 或者高级搜索页面:https://github.com/search/advanced

搜索底层实现

简单提一句:github的搜索使用了ElasticSearch集群来支持。每当有提交进入github时,索引就会建立。每当Issues和PR创建或者修改时也会建立索引。如果对ES比较熟悉的话,理解起来更快一些。

初级搜索语法

想搜啥就输入啥就好了

高级搜索语法

高级搜索语法简单而言就是:

  1. 将资源分类

    一般我们搜索的都是从代码内容中搜索。而实际上github上的资源不仅仅是代码。还有比如:Repositories、Topics、Issues and pull requests、Code、Commits、Users、Packages、Wikis。

  2. 对每一类资源的几乎每个属性都进行了搜索的支持

    比如Rspositories的名称、Stars、Created间等。比如Code的所属Repository、User、Path、Language、Filename、extension等。

  3. 支持不同属性的交并集查询,支持数值、日期等搜索

    比如:user:dytttf stars:>10 created:>2010-01-01

高级搜索示例

高级搜索限制

  • 不支持长度超过 256 个字符的查询
  • 无法使用超过五个 AND、OR 或 NOT 运算符构造查询
  • 日期格式仅支持:ISO8061,即YYYY-MM-DD 或者 YYYY-MM-DDTHH:MM:SS+00:00
  • 必须登录才能跨所有公共仓库搜索代码,非登录状态下搜索的仓库是不全的
  • 只有默认分支编索引进行代码搜索
  • 只有小于 384 KB 的文件可搜索
  • 只有少于 500,000 个文件的仓库可搜索
  • 除了 filename 搜索以外,搜索源代码时必须始终包括至少一个搜索词。 例如,搜索 language:javascript 无效,而搜索 amazing language:javascript 有效。(BUT 感觉这条限制并不存在)
  • 无法使用以下通配符作为搜索查询的一部分:. , : ; / \ ` ‘ “ = * ! ? # $ & + ^ | ~ < > ( ) { } [ ]. 搜索只会忽略这些符号

一些有趣的搜索

如何寻找最火爆的项目

通过仓库的star数来筛选:stars:>100000 然后发现第一名竟然从来没有听说过,看来是在下孤陋寡闻了。

如何寻找人气最高的大神

查找followers大于某个值的用户:followers:>=50000 然后你会发现阮一峰真NB。

如何看到当前github的体量

在搜索首页你会看到搜索框上出现下列语句之一(好像是随机的,当前时间20200222):

  • Search more than 50M users
  • Search more than 491M issues
  • Search more than 155M repositories

而在20200211,出现的是:
- Search more than 49M users

如何找到github最早注册的用户

查找用户,并按照创建时间筛选:created:<2008-01-01
如果你想知道github用户增长趋势,只要不断变化时间搜索就好了。

最后

一个简单的爬虫,让你看清github的用户趋势:https://github.com/dytttf/little_spider/tree/master/github

基础

启用隐身模式

1
2
3
4
5
6
7
8
9
10
11
12
from pyppeteer import launch


async def start_browser():
browser = await launch(**{"headless": False})
browser_context = await browser.createIncognitoBrowserContext()
page = await browser_context.newPage()

await page.goto("http://www.baidu.com")
await browser.close()
return


非隐身模式启用插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pyppeteer import launch


async def start_browser():
chrome_extension_path = "插件所在目录"
args = [
"--load-extension={}".format(chrome_extension_path),
"--disable-extensions-except={}".format(chrome_extension_path),
]

browser = await launch(**{"headless": False, "args": args})
page = await browser.newPage()

await page.goto("http://www.baidu.com")
await browser.close()
return

正文

如果是非隐身模式启用插件的话,上面的代码就可以了。但很蛋疼的是,有个场景必须得在隐身模式下运行,还需要插件。如果是人为操作的话,启用步骤如下:

  1. 打开插件管理页面 chrome://extensions/
  2. 找到需要启动的插件,点击详细信息,进入插件详情。
  3. 在详情中寻找: 在无痕模式下启用的选项并勾选

然后就可以在隐身模式下使用了。

BUT,我的目的是自动隐身模式下启用插件。Google大法开始启用。。。
一个小时后。。。WTF,反正我是没找到正常的实现方式,貌似pyppeteer不支持,不知道是不是有什么隐藏的chrome参数能用的,但找起来太费劲了,放弃。

其实在Google过程中也找到了一个办法,只是不那么优雅而已。其实很简单,就是把上面人为操作的步骤自动化一下。

代码并不是很复杂,不过在实现过程中还是学到了点新东西的。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import asyncio
from pyppeteer.browser import Browser


async def enable_extension_for_incognito(browser: Browser, extension_name: str) -> bool:
"""
在隐身模式下启用指定插件
Args:
extension_name: 插件名称

Returns:

"""
# 新开非隐身窗口并打开扩展程序页面
page = await browser.newPage()
await page.goto("chrome://extensions/")
# 执行js代码获取指定的扩展id
get_eid_js = (
"""() => {
var extension_id;
document.querySelector("extensions-manager").shadowRoot
.querySelector("extensions-item-list").shadowRoot
.querySelectorAll("extensions-item").forEach(function (e) {
var e_name = e.shadowRoot.querySelector("#name").textContent.trim();
if (e_name == "%s") {
extension_id = e.getAttribute("id")
}
})
return extension_id
}"""
% extension_name
)
extension_id = await page.evaluate(get_eid_js)
await asyncio.sleep(1)
# 进入扩展详情页面
await page.goto("chrome://extensions/?id={}".format(extension_id))
# 判断插件是否启用
get_extension_status_js = """
() => {
return document.querySelector("extensions-manager").shadowRoot
.querySelector("extensions-detail-view").shadowRoot
.querySelector("extensions-toggle-row#allow-incognito").shadowRoot
.querySelector("#crToggle").getAttribute("aria-pressed")
}
"""
r = await page.evaluate(get_extension_status_js)
if r != "true":
# 点击在无痕模式下启用
click_enable_js = """
() => {document.querySelector("extensions-manager").shadowRoot
.querySelector("extensions-detail-view").shadowRoot
.querySelector("extensions-toggle-row#allow-incognito").shadowRoot
.querySelector("#crToggle").shadowRoot
.querySelector("button").click()}
"""
await page.evaluate(click_enable_js)
await asyncio.sleep(1)
# 确认是否启用成功
r = await page.evaluate(get_extension_status_js)
assert r == "true"
await page.close()
return True

需要注意的地方有两个:

  1. shadowRoot这个东西第一次遇到。。。
  2. 浏览器有记忆功能。如果你已经启用了某个插件,那么之后再打开的时候插件就已经是启用状态了。

背景

经常做爬虫的人,应该对\x00、\x01这样的字符不陌生,网页源码里面 不经常 出现。不过一般都不深究这到底是啥。一开始我也没研究,发现之后就拿正则替换掉,简单粗暴的处理。之所以要去掉,是因为使用Python的lxml库处理的时候会抛异常。再后来,由于需要做一个通用一些的采集器,没办法再无视这个问题了,于是Google一番之后,Copy了一段代码:

1
text = re.sub("[\x00-\x1F\x7F]\s*", "", text)

不错,测试下来运行良好。完美去掉了所有控制字符。只是好景不长,过了几个月,在排查一个Xpath解析问题的时候,突然发现这段代码有很大问题。。。

控制字符简述

以下内容抄自维基百科:https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%AD%97%E7%AC%A6

 控制字符,是出现在特定的信息文本中,表示某一控制功能的字符。

好吧,这个东西最开始设计是为了给打印机用的。。。 ,现在的话,很多控制字符感觉都没啥用了。简单举几个例子:

  • 键盘上的 Backspace 产生 \x08, 用来回退一格,或者删除前一个字符
  • Return 或 Enter 键产生 \x10、\x13,用来换行。说人话就是 \n\r
  • Tab 键产生 \x09,也就是 \t

其他的就看Wiki吧,写的挺清楚的。

遇到的问题以及解决方案

例子中的这几个之所以列出,是因为我遇到的问题就是他们几个引起的。一般的网页源码中,存在的\t、\r、\n等字符,都是用来调整样式的,对数据没啥影响。但是今天就碰到了很坑的网站。它是这么写源码的:

1
<div\nclass='content'>.....</div>

卧槽。竟然用\n而不是空格来分割标签名与属性。(顺便说一句。空格是\x20,但不是控制字符)

于是。我抄的正则就有问题了。把\n给替换掉了之后,源码就变成了

1
<divclass='content'>.....</div>

这还怎么解析。。。

于是修改正则如下:

1
text = re.sub("[\x00-\x08\x11\x12\x14-\x1F\x7F\x80-\x9F]", "", text)

跳过了这几个常用字符。完美收工。
感觉应该、也许、大概、可能不会再出问题了吧!

后记

果然还是出问题了。。。

正则写顺手了。。。 应该是忽略16进制的9、10、13的,也就是\x09、\x10、\x0D。

1
text = re.sub("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "", text)