Open* 客户机怎么访问主机 NAT 后面的虚拟服务器

路由器直接分配的网段是 192.168.0.0/24

主机 192.168.0.108 安装了几台 Vmware 虚拟机,网络模式为 NAT 虚拟机网段为 192.168.204.0/24 (主机 192.168.204.1,虚拟机 192.168.204.10-20 )

现在怎么才能让主机同路由器下的其他客户机(假设 192.168.0.107 )可以访问主机服务器上的虚拟机

我在主机上安装了 Open* 服务器,主机中的虚拟机中可以 telnet 连上客户机,但是客户机只能脸上主机,连不上主机上的虚拟机

%title插图%num

附上 server.o* 和 client.o*

port 1194
proto udp
dev tun

ca ca.crt
cert server.crt
key server.key
dh dh2048.pem

server 10.8.0.0 255.255.255.0

ifconfig-pool-persist ipp.txt

push “route 192.168.204.0 255.255.255.0”
push “dhcp-option DNS 8.8.8.8”
push “dhcp-option DNS 114.114.114.114”

keepalive 10 120
cipher AES-256-CBC

persist-key
persist-tun

status open*-status.log
log-append open*.log
verb 3
explicit-exit-notify 1
client

dev tun
proto udp

remote 192.168.0.108 1194

resolv-retry infinite
nobind
persist-key
persist-tun

ca ca.crt
cert client.crt
key client.key

cipher AES-256-CBC

verb 3

UnknownSky 1
UnknownSky 45 天前 via Android
正好前段時間有這方面的研究,給個參考鏈接,實測有用。Iptables 就解決了。
UnknownSky 2
UnknownSky 45 天前 via Android
你的意思就是說 Open* clients 暴露出來嘛
UnknownSky 3
UnknownSky 45 天前 via Android
https://unix.stackexchange.com/questions/449853/port-forwarding-using-open*-client
UnknownSky 4
UnknownSky 45 天前 via Android
https://unix.stackexchange.com/questions/432175/port-forwarding-to-*-client
UnknownSky 5
UnknownSky 45 天前 via Android
還有個方法,編譯 FullconeNAT Kernel module,和 iptables,直接實現 server Fullcone
lry 6
lry 45 天前
@UnknownSky 我的目的是让 NAT 后面的虚拟机暴漏出来,这样同局域网下的其他机器可以连上去。
lry 7
lry 45 天前
@UnknownSky 端口转发可以,但是这样就得为每一台虚拟机配置一个端口转发了,我觉得用 Open* 也可以实现这个效果,而且不用记住虚拟机配置的端口转发,直接使用虚拟机 ip 。(谁要连主机上的虚拟机,就直接装个 Open* 的客户端,但是我这配置好像有问题,连不上)
XiaoxiaoPu 8
XiaoxiaoPu 45 天前
在主机上抓包看看。
看你的 server 配置,把虚拟机所在网络的路由推送给了客户端,那么客户端到虚拟机的包应该会正确的发给主机,那问题应该就是主机没有转发给虚拟机,或者虚拟机的响应没有转发给客户端。猜测可能跟 VMware 的 NAT 模式下的网络限制有关(比如防火墙等)。所以先定位下具体哪里没走下去吧。
XiaoxiaoPu 9
XiaoxiaoPu 45 天前
另外有个疑问,如你所说,客户端和服务端在一个路由器下,已经在一个子网里了,就应该不需要再连 * 了,连 * 的需求是啥?
lry 10
lry 45 天前
@XiaoxiaoPu 客户端可以直接连服务端没问题,但是服务端上的虚拟机用的 NAT 模式(装的时候未选择桥接模式),客户端和虚拟机不在同一个网段。

JmmBite 45 天前
先理解什么是 NAT:
局域网访问互联网,经过 NAT 转换肯定没问题,
那你认为互联网主机可以逆向访问局域网主机吗?肯定不行。
如果要实现,那就还需要 外网到内网的 入口 NAT ;
XiaoxiaoPu 12
XiaoxiaoPu 45 天前
@lry 所谓的网段,影响的无非就是路由。在客户端上加一条 “192.168.204.0/24 via 192.168.0.108 dev eth0” 这样的路由,也能让客户端就知道发给虚拟机的包要交给主机来转发。

不过这两种方法都是可行的,都能实现让主机作为通信的中转节点。问题关键还是 VMware 的 NAT 是否有防火墙策略。分步排查吧:客户端 -> 主机 -> 虚拟机 -> (回应) -> 主机 -> 客户端,看看链路在哪一步断掉了。
JmmBite 13
JmmBite 45 天前
所谓 NAT 就是通过软件把主机当路由器,NAT 后面的虚拟机网络就是一个小型局域网。
所谓 桥接 就是通过软件把主机当成交换机,虚拟机直接暴露给外网。

如果是双向 NAT (路由转发),那就是 桥接(交换机)的功能了。
cdlnls 14
cdlnls 45 天前 via Android
如果跨 IP 网段,数据包就需要先把数据包转发给网关(路由器),然后由路由器判断下一跳去哪。所以你要在路由器上检查一下发往虚拟机的包发去哪里了,(一般路由器默认只有一个指向 wan 口的默认路由,不会有 vmware 定义的路由,所以数据包就直接从默认的 wan 口发走了)

你可以在路由器再加一个路由指向 vmware 的虚拟机,将发往虚拟机网段的数据包先转发给 vmware 。

*后在 vmware 上再转发给虚拟机。
cdlnls 15
cdlnls 45 天前 via Android
现在就是不确定的是,vmware 能不能把包正常转发给内部的虚拟机,这个没验证过。

我自己测试过 kvm 的 router 网络,是可以访问的虚拟机的。kvm 的 router 网络和 nat 应该差不太多。
lry 16
lry 45 天前
@XiaoxiaoPu
@cdlnls 我撤去了 Open*,直接在客户机上面加了一条静态路由 `sudo route -n add -net 192.168.204.0 -netmask 255.255.255.0 192.168.0.108`,同时抓包服务器上的以太网卡和 Vmware 的 nat 网卡,发现客户机 telnet 虚拟机的 tcp 包*次握手就收不到 ack,但是 虚拟机 telnet 客户机的包连接正常。这种一般会是什么问题

图 1: 192.168.0.107 –> 192.168.204.10
https://i.loli.net/2021/02/07/UrbXvO9En2764lH.png
图 2: 192.168.204.10 –> 192.168.0.107
https://i.loli.net/2021/02/07/zIPnVkiY2758THE.png
cdlnls 17
cdlnls 45 天前 via Android
@lry 看看客户机的路由表,和对应的虚拟的网络接口。
你这个不应该是这样加的,可能要指定一下接口
XiaoxiaoPu 18
XiaoxiaoPu 45 天前
客户机加的路由应该要指定网卡的。我一般用 ip 命令,不用 route 命令,这样试试 “sudo ip route replace 192.168.204.0/24 via 192.168.0.108 dev eth0”。eth0 是客户机的网卡名称,如果不是 eth0 需要替换下。然后可以在客户机上执行“sudo ip route show” 看下当前路由是否符合预期。

主机的防火墙策略也看一下,在主机上执行 “sudo iptables-save” 查看。
ourgoder 19
ourgoder 45 天前
我也类似 25,很多也不动
lry 20
lry 45 天前
@cdlnls @ourgoder 我这边客户机是 MacOS,主机是 Win10,虚拟机才是 Debian
贴了下客户机上面的路由,感觉这个路由没有问题 ?
https://paste.ubuntu.com/p/FxNdJV2n3X/
XiaoxiaoPu 21
XiaoxiaoPu 45 天前
windows 啊,那不了解了。

干脆一把梭吧,客户端跟虚拟机都连 * 里,通过 * 的网段通信 = =
ourgoder 22
ourgoder 44 天前
@lry #20 我试试哦
fzinfz 23
fzinfz 43 天前
vmware 改 bridge 模式

https://docs.vmware.com/en/VMware-Workstation-Pro/16.0/com.vmware.ws.using.doc/GUID-3B504F2F-7A0B-415F-AE01-62363A95D052.html

With NAT, the virtual machine and the host system share a single network identity that is not visible outside the network.
fzinfz 24
fzinfz 43 天前
收回#23 回复,貌似 NAT 支持 Port Forwarding: (多年未用 VMware-Workstation,仅纸上谈兵,逃~ )

https://docs.vmware.com/en/VMware-Workstation-Pro/15.0/com.vmware.ws.using.doc/GUID-E146C894-664C-479A-9E19-484400614BED.html
lry 25
lry 43 天前
@cdlnls @XiaoxiaoPu
这个问题解决了。如果 open* 用的路由模式 (dev tun),需要 win10 服务器上开启端口转发,并且在虚拟机里面添加一条静态路由( route add -net 10.8.0.0/24 gw 192.168.204.1 )
或者使用 open* 的桥接模式( dev tap ),将 open* 的 Tap-Windows Adapter V9 虚拟网卡和 VMware 的 Vment8 NAT 虚拟网卡桥接下
lry 26
lry 43 天前
@lry #25 说错了,应该叫路由转发。https://www.jianshu.com/p/3d5ada946a31
lry 27
lry 43 天前
@fzinfz 是的,这个可以,综上所述,我觉得还是 open* 的桥接模式方便点,不需要一个个配置

解决flutter packages get 失败的*好方案

Lady and Gentleman,相信用过flutter的小伙伴大多都遇到过这种问题,入下图所示

%title插图%num

上图所示的大概 意思就是flutter sdk的路径和项目中的不一样,我们只需用找出来修改即可,有这么两种情况:

1、当我们重新下载并安装了flutter的其他版本之后,新的flutter sdk路径和原来存放的不一样,我们得通过vim ~/.bash_profile查看环境变量,*行改成新的sdk路径(也就是你要用的sdk路径)即可,如下图所示:

%title插图%num

2、如果步骤一已经ok,那下一个就是你项目中的路径了,查看项目的 .packages 文件,里面也有sdk路径,全局搜索一下,看看项目中的sdk路径是否还是用的旧的,然后也改成*新的(也就是你要用的sdk路径)就可以了

个人博客境外伺服器哪里比较划算呀 x?

用的程式是 WordPress,所以配置大概是 2C4G 保底的叭
然后…使用 Azure CDN 所以对延迟链路没有要求
价格上每个月两百以内叭…?再高一点也可以接受
还有一个要求是必须可以用卡支付(为了刷余额 x )!
大佬们有什么推介嘛?

baobao1270 1
baobao1270 32 天前
我自己用的腾讯轻量香港
icenya 2
icenya 32 天前
@baobao1270 腾讯云不能用卡付诶 x
baobao1270 3
baobao1270 32 天前
@icenya 啊这 信用卡吗
银联信用卡好像是可以绑定微信然后用微信支付的
国外的那只能找国外的了……
话说国外不是更方便吗,GCP Azure AWS 三大天王,不过我和你正好相反我没有信用卡所以没用过……
还有屌丝常用的 Vultr DigitalOcean(V2EX 就在 DO 上运行) 搬瓦工,便宜是便宜,就算延迟太高有的时候还会被阻断

可以去一个叫 HostLOC 上看看各大 IDC 的风评
icenya 4
icenya 32 天前
@baobao1270 Azure 和 AWS 咱用过的说,但是流量太贵了 x
V 家和 Linode 也用过,太泛滥了…有时候开出来的 IP 都是被墙的,虽然说不要求延迟但是 FTP 都没法上的话太麻烦了…
DigitalOcean 注册要求提交护照…就不想注册了 x
不过还是感谢提供建议啦 x…
yfwl 5
yfwl 31 天前
用卡支付直接排掉 99%商家..,不用卡支付倒是可以看看樊云,挺便宜的啊
icenya 6
icenya 31 天前
@yfwl 其实主要是想找海外商家的…对内地商家观感不是很好…
muzihuaner 7
muzihuaner 30 天前
Github 比较划算,免费?
icenya 8
icenya 30 天前
@muzihuaner
2333…
GitHub 的速度…
而且好像也只支持纯静态的叭?
luolw1998 9
luolw1998 29 天前
https://www.conoha.jp/
AsiaCloud 10
AsiaCloud 27 天前
看看测试?

Jetpack Compose UI 框架你了解多少?

Jetpack Compose 是用于构建原生界面的新款 Android 工具包。它可简化并加快 Android 的UI 开发工作。使用更少的代码、强大的工具和直观的 Kotlin API,快速构建 App 的 UI。

 

%title插图%num

需要在 Android Studio 的 Canary 版本才能体验。下载 ANDROID STUDIO CANARY 版。

https://developer.android.google.cn/studio/preview

注:作者编写此文时compose 还处于 alpha 阶段,目前已经 beta.

创建 Jetpack Compose 项目
在 Android Studio Canary 版本中已经提供了 Compose 的模板,在创建项目时选择 Empty Compose Activity 模板即可。

%title插图%num

至此,就完成一个 Compose 项目的创建。除此之外,我们也可以选择导入 Jetpack 。

Compose示例应用,操作步骤参照 Jetpack Compose Sample 操作步骤。

https://developer.android.google.cn/jetpack/compose/setup#sample
Compose 函数使用
Compose 是一种以函数为依托的声明式 UI 构建方式。比如在 MainActivity.kt 中显示一个文本。

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(“Hello Compose!”)
}
}
}

这个与使用 XML 布局的方式差别很大,setContent 块定义了 Activity 的布局。我们不使用 XML文件来定义布局内容,而是调用一个 Compose 函数,比如上面的 Text 函数。

然后 Jetpack Compose 使用自定义 Kotlin 编译器插件将这些 Compose 函数转换为应用的界面元素。

2.1 Compose 函数
Jetpack Compose 是围绕 Compose 函数构建的,在开发时只需描述应用界面的样式布局和数据依赖关系,而不必关注界面的构建过程。给一个函数添加 @Composable 注解即创建了一个Compose 函数。

注意,Compose 函数只能在其他 Compose 函数的范围内调用。下面我们将上面示例中的 Text 移动到自定义的 Compose 函数中。

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HelloCompose()
}
}

@Composable
fun HelloCompose() {
Text(“Hello Compose!”)
}
}
2.3 设置点击监听
除了使用 Text 函数,还有其它的基础函数供我们使用,比如 Button、Image 等。那么如何给 UI控件设置点击监听呢?在 Compose 框架中提供了两种方式:

对于类似 Button 函数的这种,提供了 onClick 函数式接口供外部设置点击监听;

对于类似 Text 函数这种,没有提供显式接口设置的,通过 Modifier 类设置点击监听;

Button 函数设置点击事件:

@Composable
fun TextButton() {
Button(
onClick = {
Log.d(“Andoter”, this.javaClass.name)
Toast.makeText(this@MainActivity, “Button 点击”, Toast.LENGTH_SHORT).show()
}
) {
Text(text = “Hello Compose!”, color = Color.Red)
}
}
通过设置 onClick 函数即可实现点击实现,注意 Button 函数本身没有设置文本内容,需要通过Text 函数设置显示文本内容。

Text 函数设置点击事件:

@Composable
fun ClickedText() {
val modifier = Modifier.clickable(onClick = {
Log.d(“Andoter”, this.javaClass.name)
Toast.makeText(this@MainActivity, “Button 点击”, Toast.LENGTH_SHORT).show()
})

Text(text = “Hello Compose!”,modifier = modifier.padding(10.dp))
}
通过 Modifier.clickable 的方式实现设置点击事件。Modifier 类不仅能够设置点击事件,还能够设置控件的布局属性。

clickable() :设置点击监听

padding() :在元素周围留出空间

fillMaxWidth() :使可组合项填充其父项给它的*大宽度

preferredSize() :指定元素的首选宽度和高度

2.4 预览
在 Compose 框架中为 Compose 函数提供预览能力,通过给 Compose 函数添加 @Preview注解即可进行预览。

在实际的开发中,预览函数不要发布到线上,所以*佳做法是单独创建不会被应用调用的预览函数用于查看实际效果,专门的预览函数可以提高性能,并且有利于以后更轻松地设置多个预览。
%title插图%num

布局
在 Jetpack Compose 中一切的元素都是围绕 Compose 函数展开,所以布局也是通过对应的内置Compose 函数实现。

3.1 Column 和 Row
二者的特点:

Column:使元素按照竖直方向排列;

Row:使元素按照水平方向排列;

Stack:将一个元素放在另一个元素上。

这里我们以 Column 函数作为示例。

@Preview
@Composable
fun MultiText() {
Text(text = “Hello Compose!”)
Text(“Ant 学习 Compose!”)
}

@Preview
@Composable
fun ColumnText() {
Column {
Text(text = “Hello Compose!”)
Text(“Ant 学习 Compose!”)
}
}
通过 Column 可将组件按照竖直方向排列,预览效果对比:

3.2 ScrollableRow 和 ScrollableColumn
使用 ScrollableRow 或 ScrollableColumn 可使 Row 或 Column 内的元素滚动。

@Composable
fun ProductList() {
ScrollableColumn(Modifier.fillMaxSize()) {
listOf(“Ant”, “Andoter”, “小伟”).forEach { value ->
ProductDetailView(value)
}
}
}

@Composable
fun ProductDetailView(text: String) {
val image = imageResource(id = R.drawable.header)
Column(modifier = Modifier.padding(16.dp)) {
val imageModifier = Modifier
.preferredHeight(180.dp)
.clip(shape = RoundedCornerShape(5.dp))
.fillMaxWidth()
.clickable(onClick = {
Log.d(“Ant”, “click”);
})
Image(image, modifier = imageModifier, contentScale = ContentScale.Crop)
Spacer(modifier = Modifier.preferredHeight(16.dp))
Text(“Hello Compose!”)
}
}

Compose 界面结构
通过上面的介绍,对 Compose 有了一个初步的认识,那么 Compose 函数如何绘制在屏幕上的呢?以什么样的形式展示的呢?我们使用 Layout Inspector 工具查看一个 Compose 页面。

通过左侧的布局结构可以发现,Compose 框架中已经废弃原有的 View 体系中的控件(TextView、Button、ImageView 等),而是使用 AndroidComposeView(继承ViewGroup)、ViewLayerContainer(继承 ViewGroup)和 ViewLayer(继承 View) 控件实现,其中 ViewLayer 代表每个 View 控件视图。

查看 ViewLayer 的调用关系,可以得到视图的生成关系:LayerWrapper →AndroidComposeView -> ViewLayer。

Compose 对业务的影响
Jetpack Compose 是一个适用于 Android 的新式声明性界面工具包,同时点击监听的设置方式也发生较大变化,那么对于我来说,*直观的业务影响是无法继续使用原有的插码技术进行点击事件的采集。这块需要进行调研适配。

上面提到设置点击的两种方式,本质上都是通过 Modifier 进行实现,来看下面的一个例子。

@Composable
fun ClickedText() {
val modifier = Modifier.clickable(onClick = {
Log.d(“Andoter”, this.javaClass.name)
Toast.makeText(this@MainActivity, “Button 点击”, Toast.LENGTH_SHORT).show()
})

Text(text = “Hello Compose!”, modifier = modifier.padding(10.dp))
}
通过 Modifier 给一个 Text 设置点击监听,在点击的时候弹出一个 Toast。反编译看看*后的实现。

/* access modifiers changed from: package-private */
@Metadata(mo23161bv = {1, 0, 3}, mo23164k = 3, mo23165mv = {1, 4, 0})
/* compiled from: MainActivity.kt */
public final class MainActivity$TextButton$1$1 extends Lambda implements Function0 {
private final /* synthetic */ MainActivity $this;

/* JADX INFO: super call moved to the top of the method (can break code semantics) */
MainActivity$TextButton$1$1(MainActivity mainActivity) {
super(0);
this.$this = mainActivity;
}

@Override // kotlin.jvm.functions.Function0
public final void invoke() {
Log.d(LiveLiterals$MainActivityKt.INSTANCE.mo17059x27db7fde(), this.$this.getClass().getName());
Toast.makeText(this.$this, LiveLiterals$MainActivityKt.INSTANCE.mo17064x88044b3e(), 0).show();
}
}
Kotlin 经过处理*终是转换成一个继承 Lambda 并实现 Fuction0 接口的类来托管实现点击监听。这样我们就可以总结出 Hook 条件:

kotlin.jvm.internal.Lambda 的子类

实现 kotlin.jvm.functions.Function 接口

被 public 和 final 修饰符的 invoke 方法

尽管 Hook 点找到了,但是目前还无法突破获取对应的 View,依托 View 读取的属性就无法获取。希望可以一起跟你探讨下。

总结
长期以来,Android 视图层次结构一直可以表示为界面文件树。界面更新方式是使用findViewById() 等函数遍历树,这种手动操纵视图的方式会提高出错的可能性。

在过去的几年中,移动端已开始转向声明性界面模型,比如 Flutter、Swift UI,所以 JetpackCompose框架应该也是后续发展的一个方向。

服务器&浏览器伪装的故事

今天要说的是伪装,为嘛要伪装呢?我想,首先是心虚,不够自信,比如你安全措施做的不够,你怕别人黑你的系统,所以就要伪装。其次呢,我想就是有不可告人的秘密了,比如你有竞争对手总是找你的茬,拦截你,那咋办呢,既要不失风度又要达到期望的目的,这不伪装是不错的选择。

一、对于大站点来说,只要安全做的好,当然可以正大光明的地展露自个的肌肉,所以伪装一词对他们来说根本不适用,首先来看下各大站点暴露服务器banner信息:
1.Google
%title插图%num
2.微软
%title插图%num
3.百度
%title插图%num
4.淘宝网
%title插图%num
5.京东
%title插图%num
6.新浪
%title插图%num
但是对于小点的网站,安全措施一般不够好,这样服务器信息那么暴露,曾经给一个国家单位**会做个小系统,他们的系统经常会受到攻击,当项目要上线的时候,他们的安全措施中有一项就是伪装服务器信息(把tomcat伪装成IIS),方法很简单(参看http://www.cnblogs.com/hsuchan/p/3976654.html),当然这是一种不得已的措施。但是伪装真的还挺有迷惑性的。
这样我就可以把
%title插图%num
直接改为
%title插图%num
当然我这.do的后缀也得去掉那才装更好些。

二、在很多情况下,我们需要在用户访问网站时获取他们的其他信息,比如他们的ip,操作系统,浏览器等等,当然可以从ip中粗略挖掘出用户的地理信息,其实我想说的是UserAgent,即UA信息,那么下面我们还是来看下各大浏览器的UA长的神马模样吧。

1.火狐(真命天子)
%title插图%num
2.Google
%title插图%num
3.IE11
%title插图%num
看了以后,只有一种感觉——头晕,为嘛总是抱Moilla的大腿呢?为嘛360也把自个伪装成谷歌苹果的呢?下面说下360的,至于为嘛UA里总有Mozilla,那又是一段传奇的历史,可以参看:http://www.cnblogs.com/hsuchan/p/3976888.html。 大伙都知道360和百度是个冤家,就我接触到的,在搜索,域名解析(百度有加速乐,360有网站卫士)等业务上都有竞争关系,尤其是国人在谷歌被强了以 后,使用百度的人就更多了,那么使用360浏览器的人使用baidu产品的话就有被拦截的风险,肿么办呢,上面都看到了——把自个伪装成chrome或者 Safari,那就是解决方法,算是之一吧。

以上是个人的一些浅见,欢迎交流。伪装在生活中也无处不在,从服务器的伪装上我们都知道伪装终究不是个办法,做人做事就得自信自强。从UA中,如果无法避免,那么换个方式也是不错的,总之,生活中有些事我们得变通,顺势而为。

看懂 Serverless,这一篇就够了

1. 无服务器(Serverless)计算是什么

%title插图%num

云计算涌现出很多改变传统IT架构和运维方式的新技术,比如虚拟机、容器、微服务,无论这些技术应用在哪些场景,降低成本、提升效率是云服务永恒的主题。

过去十年来,我们已经把应用和环境中很多通用的部分变成了服务。Serverless的出现,带来了跨越式变革。Serverless把主机管理、操作系统管理、资源分配、扩容,甚至是应用逻辑的全部组件都外包出去,把它们看作某种形式的商品——厂商提供服务,我们掏钱购买。

过去是“构建一个框架运行在一台服务器上,对多个事件进行响应”,Serverless则变为“构建或使用一个微服务或微功能来响应一个事件”,做到当访问时,调入相关资源开始运行,运行完成后,卸载所有开销,真正做到按需按次计费。这是云计算向纵深发展的一种自然而然的过程。

Serverless是一种构建和管理基于微服务架构的完整流程,允许你在服务部署级别而不是服务器部署级别来管理你的应用部署。它与传统架构的不同之处在于,完全由第三方管理,由事件触发,存在于无状态(Stateless)、暂存(可能只存在于一次调用的过程中)计算容器内。构建无服务器应用程序意味着开发者可以专注在产品代码上,而无须管理和操作云端或本地的服务器或运行时。Serverless真正做到了部署应用无需涉及基础设施的建设,自动构建、部署和启动服务。

国内外的各大云厂商 Amazon、微软、Google、IBM、阿里云、腾讯云、华为云相继推出Serverless产品,Serverless也从概念、愿景逐步走向落地,在各企业、公司应用开来。

2. 理解Serverless技术—FaaS和BaaS

Serverless由开发者实现的服务端逻辑运行在无状态的计算容器中,它由事件触发, 完全被第三方管理,其业务层面的状态则被开发者使用的数据库和存储资源所记录。Serverless涵盖了很多技术,分为两类:FaaS和BaaS。

2.1 FaaS(Function as a Service,函数即服务)

FaaS意在无须自行管理服务器系统或自己的服务器应用程序,即可直接运行后端代码。其中所指的服务器应用程序,是该技术与容器和PaaS(平台即服务)等其他现代化架构*大的差异。

FaaS可以取代一些服务处理服务器(可能是物理计算机,但*对需要运行某种应用程序),这样不仅不需要自行供应服务器,也不需要全时运行应用程序。

FaaS产品不要求必须使用特定框架或库进行开发。在语言和环境方面,FaaS函数就是常规的应用程序。例如AWS Lambda的函数可以通过Javascript、Python以及任何JVM语言(Java、Clojure、Scala)等实现。然而Lambda函数也可以执行任何捆绑有所需部署构件的进程,因此可以使用任何语言,只要能编译为Unix进程即可。FaaS函数在架构方面确实存在一定的局限,尤其是在状态和执行时间方面。

在迁往FaaS的过程中,唯一需要修改的代码是“主方法/启动”代码,其中可能需要删除顶级消息处理程序的相关代码(“消息监听器接口”的实现),但这可能只需要更改方法签名即可。在FaaS的世界中,代码的其余所有部分(例如向数据库执行写入的代码)无须任何变化。

相比传统系统,部署方法会有较大变化 – 将代码上传至FaaS供应商,其他事情均可由供应商完成。目前这种方式通常意味着需要上传代码的全新定义(例如上传zip或JAR文件),随后调用一个专有API发起更新过程。

FaaS中的函数可以通过供应商定义的事件类型触发。对于亚马逊AWS,此类触发事件可以包括S3(文件)更新、时间(计划任务),以及加入消息总线的消息(例如Kinesis)。通常你的函数需要通过参数指定自己需要绑定到的事件源。

大部分供应商还允许函数作为对传入Http请求的响应来触发,通常这类请求来自某种该类型的API网关(例如AWS API网关、Webtask)。

2.2 BaaS(Backend as a Service,后端即服务)

BaaS(Backend as a Service,后端即服务)是指我们不再编写或管理所有服务端组件,可以使用领域通用的远程组件(而不是进程内的库)来提供服务。理解BaaS,需要搞清楚它与PaaS的区别。

首先BaaS并非PaaS,它们的区别在于:PaaS需要参与应用的生命周期管理,BaaS则仅仅提供应用依赖的第三方服务。典型的PaaS平台需要提供手段让开发者部署和配置应用,例如自动将应用部署到Tomcat容器中,并管理应用的生命周期。BaaS不包含这些内容,BaaS只以API的方式提供应用依赖的后端服务,例如数据库和对象存储。BaaS可以是公共云服务商提供的,也可以是第三方厂商提供的。其次从功能上讲,BaaS可以看作PaaS的一个子集,即提供第三方依赖组件的部分。

BaaS服务还允许我们依赖其他人已经实现的应用逻辑。对于这点,认证就是一个很好的例子。很多应用都要自己编写实现注册、登录、密码管理等逻辑的代码,而对于不同的应用这些代码往往大同小异。完全可以把这些重复性的工作提取出来,再做成外部服务,而这正是Auth0和Amazon Cognito等产品的目标。它们能实现全面的认证和用户管理,开发团队再也不用自己编写或者管理实现这些功能的代码。

3. 无服务器(Serverless)计算如何工作?

与使用虚拟机或一些底层的技术来部署和管理应用程序相比,无服务器计算提供了一种更高级别的抽象。因为它们有不同的抽象和“触发器”的集合。

拿计算来讲,这种抽象有一个特定函数和抽象的触发器,它通常是一个事件。以数据库为例,这种抽象也许是一个表,而触发器相当于表的查询或搜索,或者通过在表中做一些事情而生成的事件。

比如一款手机游戏,允许用户在不同的平台上为全球顶级玩家使用高分数表。当请求此信息时,请求从应用程序到API接口。API接口或许会触发AWS的Lambda函数,或者无服务器函数,这些函数再从数据库表中获取到数据流,返回包含前五名分数的一定格式的数据。

一旦构建完成,应用程序的功能就可以在基于移动和基于 Web 的游戏版本中重用。

这跟设置服务器不同,不是必须要有Amazon EC2实例或服务器,然后等待请求。环境由事件触发,而响应事件所需的逻辑只在响应时执行。这意味着,运行函数的资源只有在函数运行时被创建,产生一种非常高效的方法来构建应用程序。

4. 无服务器(Serverless)适用于哪些场景?

%title插图%num

在现阶段,Serverless主要应用在以下几个场景。首先在Web及移动端服务中,可以整合API网关和Serverles服务构建Web及移动后端,帮助开发者构建可弹性扩展、高可用的移动或 Web后端应用服务。在IoT场景下可高效的处理实时流数据,由设备产生海量的实时信息流数据,通过Serverles服务分类处理并写入后端处理。另外在实时媒体资讯内容处理场景里,用户上传的音视频到对象存储OBS,通过上传事件触发多个函数,分别完成高清转码、音频转码等功能,满足用户对实时性和并发能力的高要求。无服务器计算还适合于任何事件驱动的各种不同的用例,这包括物联网,移动应用,基于网络的应用程序和聊天机器人等。这里简单说两个场景,方便大家思考。

4.1 场景一:应用负载有显著的波峰波谷

Serverless 应用成功与否的评判标准并不是公司规模的大小,而是其业务背后的具体技术问题,比如业务波峰波谷明显,如何实现削峰填谷。一个公司的业务负载具有波峰波谷时,机器资源要按照峰值需求预估;而在波谷时期机器利用率则明显下降,因为不能进行资源复用而导致浪费。

业界普遍共识是,当自有机器的利用率小于 30%,使用 Serverless 后会有显著的效率提升。对于云服务厂商,在具备了足够多的用户之后,各种波峰波谷叠加后平稳化,聚合之后资源复用性更高。比如,外卖企业负载高峰是在用餐时期,安防行业的负载高峰则是夜间,这是受各个企业业务定位所限的;而对于一个成熟的云服务厂商,如果其平台足够大,用户足够多,是不应该有明显的波峰波谷现象的。

4.2 场景二:典型用例 – 基于事件的数据处理

视频处理的后端系统,常见功能需求如下:视频转码、抽取数据、人脸识别等,这些均为通用计算任务,可由函数计算执行。

开发者需要自己写出实现逻辑,再将任务按照控制流连接起来,每个任务的具体执行由云厂商来负责。如此,开发变得更便捷,并且构建的系统天然高可用、实时弹性伸缩,用户不需要关心机器层面问题。

5. Serverless 的问题

对于企业来说,支持Serverless计算的平台可以节省大量时间和成本,同时可以释放员工,让开发者得以开展更有价值的工作,而不是管理基础设施。另一方面可以提高敏捷度,更快速地推出新应用和新服务,进而提高客户满意度。但是Serverless不是完美的,它也存在一些问题,需要慎重应用在生产环境。

5.1 不适合长时间运行应用

Serverless 在请求到来时才运行。这意味着,当应用不运行的时候就会进入 “休眠状态”,下次当请求来临时,应用将会需要一个启动时间,即冷启动时间。如果你的应用需要一直长期不间断的运行、处理大量的请求,那么你可能就不适合采用 Serverless 架构。如果你通过 CRON 的方式或者 CloudWatch 来定期唤醒应用,又会比较消耗资源。这就需要我们对它做优化,如果频繁调用,这个资源将会常驻内存,*次冷启之后,就可以一直服务,直到一段时间内没有新的调用请求进来,则会转入“休眠”状态,甚至被回收,从而不消耗任何资源。

5.2 完全依赖于第三方服务

当你所在的企业云环境已经有大量的基础设施的时候,Serverless 对于你来说,并不是一个好东西。当我们采用某云服务厂商的 Serverless 架构时,我们就和该服务供应商绑定了,那么我们再将服务迁到别的云服务商上就没有那么容易了。

我们需要修改一下系列的底层代码,能采取的应对方案,便是建立隔离层。这意味着,在设计应用的时候,就需要隔离 API 网关、隔离数据库层,考虑到市面上还没有成熟的 ORM 工具,让你既支持Firebase,又支持 DynamoDB等等。这些也将带给我们一些额外的成本,可能带来的问题会比解决的问题多。

5.3 缺乏调试和开发工具

当我使用 Serverless Framework 的时候,遇到了这样的问题:缺乏调试和开发工具。后来,我发现了 serverless-offline、dynamodb-local 等一系列插件之后,问题有一些改善。然而,对于日志系统来说,这仍然是一个艰巨的挑战。

每次你调试的时候,你需要一遍又一遍地上传代码。而每次上传的时候,你就好像是在部署服务器,并不能总是快速地定位出问题在哪。后来,找了一个类似于 log4j 这样的可以分级别记录日志的 Node.js 库 winston。它可以支持 error、warn、info、verbose、debug、silly 六个不同级别的日志,再结合大数据进行日志分析过滤,才能快速定位问题。

5.4 构建复杂

Serverless 很便宜,但是这并不意味着它很简单。AWS Lambda的 CloudFormation配置是如此的复杂,并且难以阅读及编写(JSON 格式),虽然CloudFomation提供了Template模板,但想要使用它的话,需要创建一个Stack,在Stack中指定你要使用的Template,然后aws才会按照Template中的定义来创建及初始化资源。

而Serverless Framework的配置更加简单,采用的是 YAML 格式。在部署的时候,Serverless Framework 会根据我们的配置生成 CloudFormation 配置。然而这也并非是一个真正用于生产的配置,真实的应用场景远远比这复杂。

6. 总结

云计算经过这么多年的发展,逐渐进化到用户仅需关注业务和所需的资源。比如,通过K8S这类编排工具,用户只要关注自己的计算和需要的资源(CPU、内存等)就行了,不需要操心到机器这一层。

Serverless架构让人们不再操心运行所需的资源,只需关注自己的业务逻辑,并且为实际消耗的资源付费。可以说,随着Serverless架构的兴起,真正的云计算时代才算到来了。

任何新概念新技术的落地,本质上都是要和具体业务去结合,去真正解决具体问题。虽然Serverless很多地方不成熟,亟待完善。不过Serverless自身的优越特性,对于开发者来说,吸引力是巨大的。相信随着技术的飞速发展,Serverless在未来还有无限可能!

iOS笔记–#include和#import的区别、#import和@class的区别

申明:此为本人学习笔记,若有纰漏错误之处的可留言共同探讨

#include和#import的区别:

1. #include和#import的作用都是导入头文件,

2. 只是#import是oc语言的头文件导入,它能避免重复导入,确保头文件只会被导入一次,

3. #include如果稍不警惕就会重复导入,出现相互包含的编译错误,因此开发的时候,推荐开发时使用#import。
#import和@class的区别:

1. #import是导入头文件,整个类的所有信息都会包含在内,包括实例变量和方法;

2. @class是通知编译器,声明的那个名称就是个类,不过类的内容是什么,怎么定义的暂时不需要知道。在@class之后,后面还要用到这个类的属性时,需要重新再#import一次,否则调用不出来。

3. 使用的时候,通常是在.h文件中使用@class引入新类,在.m文件中使用#import,这样做的好处在于减少编译时间。也防止了相互包含的编译错误。
————————————————

新版 Android 微云不给权限不让用?

新版 Android 微云不给权限不让用?
App Ops 也治不了,死活要电话权限,这个权限该给吗?

edsheeran 1
edsheeran 2019-03-02 16:41:38 +08:00 via iPhone
dropbox
google drive
microsoft onedrive
mega
ltux 2
ltux 2019-03-02 16:57:31 +08:00
咦,怎么实现干翻 App Ops 的?
miao 3
miao 2019-03-02 18:43:44 +08:00 via Android
试试旧版?
mengyang624 4
mengyang624 2019-03-02 18:55:13 +08:00
试试 XPrivacyLua
nanaw 5
nanaw 2019-03-02 20:53:20 +08:00 via Android ❤️ 2
当然不该给。美团也这样,二次校验获取到的数据了。那么 appops 和 xprivacy 的非专业版也不行了。可用试试 xapm 的隐匿(不太靠谱),或者是有伪装机型功能的容器分身。
BTW,做这功能的程序猿 /产品请?‍?‍?‍?爆炸
orangeade 6
orangeade 2019-03-02 21:15:34 +08:00 via Android ❤️ 2
virtual Xposed / island,看来腾讯要作恶到底了,不少应用都检测 appops
732870147 7
732870147 2019-03-02 22:56:18 +08:00
看来我得考虑下一台手机入 iPhone 了…
reself 8
reself 2019-03-02 23:11:58 +08:00 via Android
妈卖批的马化腾
yjxlovejsn 9
yjxlovejsn 2019-03-02 23:38:19 +08:00 ❤️ 1
我就是因为不给不让用才换的苹果
bclerdx 10
bclerdx 2019-03-03 07:36:34 +08:00 via Android
没辙啊

alfchin 11
alfchin 2019-03-03 18:15:24 +08:00 via Android
谷歌爸爸在后面撑腰,不给权限就别用了
evam 12
evam 2019-03-04 14:05:47 +08:00
iPhone

你知道怎么利用利用爬虫爬网页(Python代码)必看

手教你利用爬虫爬网页(Python代码)%title插图%num

本文主要分为两个部分:一部分是网络爬虫的概述,帮助大家详细了解网络爬虫;另一部分是HTTP请求的Python实现,帮助大家了解Python中实现HTTP请求的各种方式,以便具备编写HTTP网络程序的能力。

01

网络爬虫概述

接下来从网络爬虫的概念、用处与价值和结构等三个方面,让大家对网络爬虫有一个基本的了解。

1. 网络爬虫及其应用

随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战,网络爬虫应运而生。网络爬虫(又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。下面通过图3-1展示一下网络爬虫在互联网中起到的作用:

640?wx_fmt=png

▲图3-1 网络爬虫

网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:通用网络爬虫、聚焦网络爬虫、增量式网络爬虫、深层网络爬虫。实际的网络爬虫系统通常是几种爬虫技术相结合实现的。

搜索引擎(Search Engine),例如传统的通用搜索引擎baidu、Yahoo和Google等,是一种大型复杂的网络爬虫,属于通用性网络爬虫的范畴。但是通用性搜索引擎存在着一定的局限性:

不同领域、不同背景的用户往往具有不同的检索目的和需求,通用搜索引擎所返回的结果包含大量用户不关心的网页。

通用搜索引擎的目标是尽可能大的网络覆盖率,有限的搜索引擎服务器资源与无限的网络数据资源之间的矛盾将进一步加深。

万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。

通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询。

为了解决上述问题,定向抓取相关网页资源的聚焦爬虫应运而生。

聚焦爬虫是一个自动下载网页的程序,它根据既定的抓取目标,有选择地访问万维网上的网页与相关的链接,获取所需要的信息。与通用爬虫不同,聚焦爬虫并不追求大的覆盖,而将目标定为抓取与某一特定主题内容相关的网页,为面向主题的用户查询准备数据资源。

说完了聚焦爬虫,接下来再说一下增量式网络爬虫。增量式网络爬虫是指对已下载网页采取增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。

和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。

例如:想获取赶集网的招聘信息,以前爬取过的数据没有必要重复爬取,只需要获取更新的招聘数据,这时候就要用到增量式爬虫。

*后说一下深层网络爬虫。Web页面按存在方式可以分为表层网页和深层网页。表层网页是指传统搜索引擎可以索引的页面,以超链接可以到达的静态网页为主构成的Web页面。深层网络是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获得的Web页面。

例如用户登录或者注册才能访问的页面。可以想象这样一个场景:爬取贴吧或者论坛中的数据,必须在用户登录后,有权限的情况下才能获取完整的数据。

2. 网络爬虫结构

下面用一个通用的网络爬虫结构来说明网络爬虫的基本工作流程,如图3-4所示。

640?wx_fmt=png

▲图3-4 网络爬虫结构

网络爬虫的基本工作流程如下:

首先选取一部分精心挑选的种子URL。

将这些URL放入待抓取URL队列。

从待抓取URL队列中读取待抓取队列的URL,解析DNS,并且得到主机的IP,并将URL对应的网页下载下来,存储进已下载网页库中。此外,将这些URL放进已抓取URL队列。

分析已抓取URL队列中的URL,从已下载的网页数据中分析出其他URL,并和已抓取的URL进行比较去重,*后将去重过的URL放入待抓取URL队列,从而进入下一个循环。

02

HTTP请求的Python实现

通过上面的网络爬虫结构,我们可以看到读取URL、下载网页是每一个爬虫必备而且关键的功能,这就需要和HTTP请求打交道。接下来讲解Python中实现HTTP请求的三种方式:urllib2/urllib、httplib/urllib以及Requests。

1. urllib2/urllib实现

urllib2和urllib是Python中的两个内置模块,要实现HTTP功能,实现方式是以urllib2为主,urllib为辅。

1.1 首先实现一个完整的请求与响应模型

urllib2提供一个基础函数urlopen,通过向指定的URL发出请求来获取数据。*简单的形式是:

import urllib2
response=urllib2.urlopen(‘http://www.zhihu.com’)
html=response.read()
print html

其实可以将上面对http://www.zhihu.com的请求响应分为两步,一步是请求,一步是响应,形式如下:

import urllib2
# 请求
request=urllib2.Request(‘http://www.zhihu.com’)
# 响应
response = urllib2.urlopen(request)
html=response.read()
print html

上面这两种形式都是GET请求,接下来演示一下POST请求,其实大同小异,只是增加了请求数据,这时候用到了urllib。示例如下:

import urllib
import urllib2
url = ‘http://www.xxxxxx.com/login’
postdata = {‘username’ : ‘qiye’,
‘password’ : ‘qiye_pass’}
# info 需要被编码为urllib2能理解的格式,这里用到的是urllib
data = urllib.urlencode(postdata)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
html = response.read()

但是有时会出现这种情况:即使POST请求的数据是对的,但是服务器拒*你的访问。这是为什么呢?问题出在请求中的头信息,服务器会检验请求头,来判断是否是来自浏览器的访问,这也是反爬虫的常用手段。

1.2 请求头headers处理

将上面的例子改写一下,加上请求头信息,设置一下请求头中的User-Agent域和Referer域信息。

import urllib
import urllib2
url = ‘http://www.xxxxxx.com/login’
user_agent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
referer=’http://www.xxxxxx.com/’
postdata = {‘username’ : ‘qiye’,
‘password’ : ‘qiye_pass’}
# 将user_agent,referer写入头信息
headers={‘User-Agent’:user_agent,’Referer’:referer}
data = urllib.urlencode(postdata)
req = urllib2.Request(url, data,headers)
response = urllib2.urlopen(req)
html = response.read()

也可以这样写,使用add_header来添加请求头信息,修改如下:

import urllib
import urllib2
url = ‘http://www.xxxxxx.com/login’
user_agent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
referer=’http://www.xxxxxx.com/’
postdata = {‘username’ : ‘qiye’,
‘password’ : ‘qiye_pass’}
data = urllib.urlencode(postdata)
req = urllib2.Request(url)
# 将user_agent,referer写入头信息
req.add_header(‘User-Agent’,user_agent)
req.add_header(‘Referer’,referer)
req.add_data(data)
response = urllib2.urlopen(req)
html = response.read()

对有些header要特别留意,服务器会针对这些header做检查,例如:

User-Agent:有些服务器或Proxy会通过该值来判断是否是浏览器发出的请求。

Content-Type:在使用REST接口时,服务器会检查该值,用来确定HTTP Body中的内容该怎样解析。在使用服务器提供的RESTful或SOAP服务时,Content-Type设置错误会导致服务器拒*服务。常见的取值有:application/xml(在XML RPC,如RESTful/SOAP调用时使用)、application/json(在JSON RPC调用时使用)、application/x-www-form-urlencoded(浏览器提交Web表单时使用)。

Referer:服务器有时候会检查防盗链。

1.3 Cookie处理

urllib2对Cookie的处理也是自动的,使用CookieJar函数进行Cookie的管理。如果需要得到某个Cookie项的值,可以这么做:

import urllib2
import cookielib
cookie = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
response = opener.open(‘http://www.zhihu.com’)
for item in cookie:
print item.name+’:’+item.value

但是有时候会遇到这种情况,我们不想让urllib2自动处理,我们想自己添加Cookie的内容,可以通过设置请求头中的Cookie域来做:

import urllib2
opener = urllib2.build_opener()
opener.addheaders.append( ( ‘Cookie’, ’email=’ + “xxxxxxx@163.com” ) )
req = urllib2.Request( “http://www.zhihu.com/” )
response = opener.open(req)
print response.headers
retdata = response.read()

1.4 Timeout设置超时

在Python2.6之前的版本,urllib2的API并没有暴露Timeout的设置,要设置Timeout值,只能更改Socket的全局Timeout值。示例如下:

import urllib2
import socket
socket.setdefaulttimeout(10) # 10 秒钟后超时
urllib2.socket.setdefaulttimeout(10) # 另一种方式

在Python2.6及新的版本中,urlopen函数提供了对Timeout的设置,示例如下:

import urllib2
request=urllib2.Request(‘http://www.zhihu.com’)
response = urllib2.urlopen(request,timeout=2)
html=response.read()
print html

1.5 获取HTTP响应码

对于200 OK来说,只要使用urlopen返回的response对象的getcode()方法就可以得到HTTP的返回码。但对其他返回码来说,urlopen会抛出异常。这时候,就要检查异常对象的code属性了,示例如下:

import urllib2
try:
response = urllib2.urlopen(‘http://www.google.com’)
print response
except urllib2.HTTPError as e:
if hasattr(e, ‘code’):
print ‘Error code:’,e.code

1.6 重定向

urllib2默认情况下会针对HTTP 3XX返回码自动进行重定向动作。要检测是否发生了重定向动作,只要检查一下Response的URL和Request的URL是否一致就可以了,示例如下:

import urllib2
response = urllib2.urlopen(‘http://www.zhihu.cn’)
isRedirected = response.geturl() == ‘http://www.zhihu.cn’

如果不想自动重定向,可以自定义HTTPRedirectHandler类,示例如下:

import urllib2
class RedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_301(self, req, fp, code, msg, headers):
pass
def http_error_302(self, req, fp, code, msg, headers):
result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code,
msg, headers)
result.status = code
result.newurl = result.geturl()
return result
opener = urllib2.build_opener(RedirectHandler)
opener.open(‘http://www.zhihu.cn’)

1.7 Proxy的设置

在做爬虫开发中,必不可少地会用到代理。urllib2默认会使用环境变量http_proxy来设置HTTP Proxy。但是我们一般不采用这种方式,而是使用ProxyHandler在程序中动态设置代理,示例代码如下:

import urllib2
proxy = urllib2.ProxyHandler({‘http’: ‘127.0.0.1:8087’})
opener = urllib2.build_opener([proxy,])
urllib2.install_opener(opener)
response = urllib2.urlopen(‘http://www.zhihu.com/’)
print response.read()

这里要注意的一个细节,使用urllib2.install_opener()会设置urllib2的全局opener,之后所有的HTTP访问都会使用这个代理。这样使用会很方便,但不能做更细粒度的控制,比如想在程序中使用两个不同的Proxy设置,这种场景在爬虫中很常见。比较好的做法是不使用install_opener去更改全局的设置,而只是直接调用opener的open方法代替全局的urlopen方法,修改如下:

import urllib2
proxy = urllib2.ProxyHandler({‘http’: ‘127.0.0.1:8087’})
opener = urllib2.build_opener(proxy,)
response = opener.open(“http://www.zhihu.com/”)
print response.read()

2. httplib/urllib实现

httplib模块是一个底层基础模块,可以看到建立HTTP请求的每一步,但是实现的功能比较少,正常情况下比较少用到。在Python爬虫开发中基本上用不到,所以在此只是进行一下知识普及。下面介绍一下常用的对象和函数:

创建HTTPConnection对象:

class httplib.HTTPConnection(host[, port[, strict[, timeout[, source_address]]]])。

发送请求:

HTTPConnection.request(method, url[, body[, headers]])。

获得响应:

HTTPConnection.getresponse()。

读取响应信息:

HTTPResponse.read([amt])。

获得指定头信息:

HTTPResponse.getheader(name[, default])。

获得响应头(header, value)元组的列表:

HTTPResponse.getheaders()。

获得底层socket文件描述符:

HTTPResponse.fileno()。

获得头内容:

HTTPResponse.msg。

获得头http版本:

HTTPResponse.version。

获得返回状态码:

HTTPResponse.status。

获得返回说明:

HTTPResponse.reason。

接下来演示一下GET请求和POST请求的发送,首先是GET请求的示例,如下所示:

import httplib
conn =None
try:
conn = httplib.HTTPConnection(“www.zhihu.com”)
conn.request(“GET”, “/”)
response = conn.getresponse()
print response.status, response.reason
print ‘-‘ * 40
headers = response.getheaders()
for h in headers:
print h
print ‘-‘ * 40
print response.msg
except Exception,e:
print e
finally:
if conn:
conn.close()

POST请求的示例如下:

import httplib, urllib
conn = None
try:
params = urllib.urlencode({‘name’: ‘qiye’, ‘age’: 22})
headers = {“Content-type”: “application/x-www-form-urlencoded”
, “Accept”: “text/plain”}
conn = httplib.HTTPConnection(“www.zhihu.com”, 80, timeout=3)
conn.request(“POST”, “/login”, params, headers)
response = conn.getresponse()
print response.getheaders() # 获取头信息
print response.status
print response.read()
except Exception, e:
print e
finally:
if conn:
conn.close()

3. 更人性化的Requests

Python中Requests实现HTTP请求的方式,是本人*力推荐的,也是在Python爬虫开发中*为常用的方式。Requests实现HTTP请求非常简单,操作更加人性化。

Requests库是第三方模块,需要额外进行安装。Requests是一个开源库,源码位于:

GitHub: https://github.com/kennethreitz/requests

希望大家多多支持作者。

使用Requests库需要先进行安装,一般有两种安装方式:

使用pip进行安装,安装命令为:pip install requests,不过可能不是*新版。

直接到GitHub上下载Requests的源代码,下载链接为:

https://github.com/kennethreitz/requests/releases

将源代码压缩包进行解压,然后进入解压后的文件夹,运行setup.py文件即可。

如何验证Requests模块安装是否成功呢?在Python的shell中输入import requests,如果不报错,则是安装成功。如图3-5所示。

640?wx_fmt=png

▲图3-5 验证Requests安装

3.1 首先还是实现一个完整的请求与响应模型

以GET请求为例,*简单的形式如下:

import requests
r = requests.get(‘http://www.baidu.com’)
print r.content

大家可以看到比urllib2实现方式的代码量少。接下来演示一下POST请求,同样是非常简短,更加具有Python风格。示例如下:

import requests
postdata={‘key’:’value’}
r = requests.post(‘http://www.xxxxxx.com/login’,data=postdata)
print r.content

HTTP中的其他请求方式也可以用Requests来实现,示例如下:

r = requests.put(‘http://www.xxxxxx.com/put’, data = {‘key’:’value’})
r = requests.delete(‘http://www.xxxxxx.com/delete’)
r = requests.head(‘http://www.xxxxxx.com/get’)
r = requests.options(‘http://www.xxxxxx.com/get’)

接着讲解一下稍微复杂的方式,大家肯定见过类似这样的URL:

http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

就是在网址后面紧跟着“?”,“?”后面还有参数。那么这样的GET请求该如何发送呢?肯定有人会说,直接将完整的URL带入即可,不过Requests还提供了其他方式,示例如下:

import requests
payload = {‘Keywords’: ‘blog:qiyeboy’,’pageindex’:1}
r = requests.get(‘http://zzk.cnblogs.com/s/blogpost’, params=payload)
print r.url

通过打印结果,我们看到*终的URL变成了:

http://zzk.cnblogs.com/s/blogpost?Keywords=blog:qiyeboy&pageindex=1

3.2 响应与编码

还是从代码入手,示例如下:

import requests
r = requests.get(‘http://www.baidu.com’)
print ‘content–>’+r.content
print ‘text–>’+r.text
print ‘encoding–>’+r.encoding
r.encoding=’utf-8’
print ‘new text–>’+r.text

其中r.content返回的是字节形式,r.text返回的是文本形式,r.encoding返回的是根据HTTP头猜测的网页编码格式。

输出结果中:“text–>”之后的内容在控制台看到的是乱码,“encoding–>”之后的内容是ISO-8859-1(实际上的编码格式是UTF-8),由于Requests猜测编码错误,导致解析文本出现了乱码。Requests提供了解决方案,可以自行设置编码格式,r.encoding=’utf-8’设置成UTF-8之后,“new text–>”的内容就不会出现乱码。

但是这种手动的方式略显笨拙,下面提供一种更加简便的方式:chardet,这是一个非常优秀的字符串/文件编码检测模块。安装方式如下:

pip install chardet

安装完成后,使用chardet.detect()返回字典,其中confidence是检测精确度,encoding是编码形式。示例如下:

import requests
r = requests.get(‘http://www.baidu.com’)
print chardet.detect(r.content)
r.encoding = chardet.detect(r.content)[‘encoding’]
print r.text

直接将chardet探测到的编码,赋给r.encoding实现解码,r.text输出就不会有乱码了。

除了上面那种直接获取全部响应的方式,还有一种流模式,示例如下:

import requests
r = requests.get(‘http://www.baidu.com’,stream=True)
print r.raw.read(10)

设置stream=True标志位,使响应以字节流方式进行读取,r.raw.read函数指定读取的字节数。

3.3 请求头headers处理

Requests对headers的处理和urllib2非常相似,在Requests的get函数中添加headers参数即可。示例如下:

import requests
user_agent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
headers={‘User-Agent’:user_agent}
r = requests.get(‘http://www.baidu.com’,headers=headers)
print r.content

3.4 响应码code和响应头headers处理

获取响应码是使用Requests中的status_code字段,获取响应头使用Requests中的headers字段。示例如下:

import requests
r = requests.get(‘http://www.baidu.com’)
if r.status_code == requests.codes.ok:
print r.status_code# 响应码
print r.headers# 响应头
print r.headers.get(‘content-type’)# 推荐使用这种获取方式,获取其中的某个字段
print r.headers[‘content-type’]# 不推荐使用这种获取方式
else:
r.raise_for_status()

上述程序中,r.headers包含所有的响应头信息,可以通过get函数获取其中的某一个字段,也可以通过字典引用的方式获取字典值,但是不推荐,因为如果字段中没有这个字段,第二种方式会抛出异常,*种方式会返回None。

r.raise_for_status()是用来主动地产生一个异常,当响应码是4XX或5XX时,raise_for_status()函数会抛出异常,而响应码为200时,raise_for_status()函数返回None。

3.5 Cookie处理

如果响应中包含Cookie的值,可以如下方式获取Cookie字段的值,示例如下:

import requests
user_agent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
headers={‘User-Agent’:user_agent}
r = requests.get(‘http://www.baidu.com’,headers=headers)
# 遍历出所有的cookie字段的值
for cookie in r.cookies.keys():
print cookie+’:’+r.cookies.get(cookie)

如果想自定义Cookie值发送出去,可以使用以下方式,示例如下:

import requests
user_agent = ‘Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)’
headers={‘User-Agent’:user_agent}
cookies = dict(name=’qiye’,age=’10’)
r = requests.get(‘http://www.baidu.com’,headers=headers,cookies=cookies)
print r.text

还有一种更加高级,且能自动处理Cookie的方式,有时候我们不需要关心Cookie值是多少,只是希望每次访问的时候,程序自动把Cookie的值带上,像浏览器一样。Requests提供了一个session的概念,在连续访问网页,处理登录跳转时特别方便,不需要关注具体细节。使用方法示例如下:

import Requests
oginUrl = ‘http://www.xxxxxxx.com/login’
s = requests.Session()
#首先访问登录界面,作为游客,服务器会先分配一个cookie
r = s.get(loginUrl,allow_redirects=True)
datas={‘name’:’qiye’,’passwd’:’qiye’}
#向登录链接发送post请求,验证成功,游客权限转为会员权限
r = s.post(loginUrl, data=datas,allow_redirects= True)
print r.text

上面的这段程序,其实是正式做Python开发中遇到的问题,如果没有*步访问登录的页面,而是直接向登录链接发送Post请求,系统会把你当做非法用户,因为访问登录界面时会分配一个Cookie,需要将这个Cookie在发送Post请求时带上,这种使用Session函数处理Cookie的方式之后会很常用。

3.6 重定向与历史信息

处理重定向只是需要设置一下allow_redirects字段即可,例如:

r=requests.get(‘http://www.baidu.com’,allow_redirects=True)

将allow_redirects设置为True,则是允许重定向;设置为False,则是禁止重定向。如果是允许重定向,可以通过r.history字段查看历史信息,即访问成功之前的所有请求跳转信息。示例如下:

import requests
r = requests.get(‘http://github.com’)
print r.url
print r.status_code
print r.history

打印结果如下:

https://github.com/
200
(,)

上面的示例代码显示的效果是访问GitHub网址时,会将所有的HTTP请求全部重定向为HTTPS。

3.7 超时设置

超时选项是通过参数timeout来进行设置的,示例如下:

requests.get(‘http://github.com’, timeout=2)

3.8 代理设置

使用代理Proxy,你可以为任意请求方法通过设置proxies参数来配置单个请求:

import requests
proxies = {
“http”: “http://0.10.1.10:3128”,
“https”: “http://10.10.1.10:1080”,
}
requests.get(“http://example.org”, proxies=proxies)

也可以通过环境变量HTTP_PROXY和HTTPS_PROXY?来配置代理,但是在爬虫开发中不常用。你的代理需要使用HTTP Basic Auth,可以使用http://user:password@host/语法:

proxies = {
“http”: “http://user:pass@10.10.1.10:3128/”,
}

03

小结

本文主要讲解了网络爬虫的结构和应用,以及Python实现HTTP请求的几种方法。希望大家对本文中的网络爬虫工作流程和Requests实现HTTP请求的方式重点吸收消化。

安卓版长按微信图标弹出菜单了?

Android安卓版长按微信图标弹出菜单了?
1
flywith24 6 小时 8 分钟前
这个叫 Shortcut 表现类似于 iOS 的 3D Touch,不过不是硬件级别的支持。应该是 Android 7.1 以上才能用,也与 Launcher 有关。app 方可以配置 Shortcut 弹出的菜单
FantaMole 2
FantaMole 6 小时 7 分钟前
虽然不知道你用的是什么牌子的,但是我手上的这台 哇喂 mate 的大部分 App 似乎在很早就有长按出菜单的功能了
idyu 3
idyu 6 小时 1 分钟前 ❤️ 9
楼主应该不是惊讶于安卓的这个功能,是惊讶微信居然更新了这个功能,
在大部分 APP 都有这个功能的时候只有微信我行我素了很长一段时间
UnknownR 4
UnknownR 6 小时 0 分钟前
@FantaMole 哈哈哈哇喂就很灵性了,我记得 miui 也有,但是部分 app 适配了才会有
Muniesa 5
Muniesa 6 小时 0 分钟前
试了一下确实有,张小龙终于想起来 Android 有这个功能了
66beta 6
66beta 5 小时 56 分钟前
你这一说,确实啊,长按出现了通知,还是小龙会玩
732870147 7
732870147 5 小时 52 分钟前 via Android
@FantaMole 你是广东人吗,这叫法太逗了
ETO 8
ETO 5 小时 41 分钟前
我草
wolfan 9
wolfan 5 小时 40 分钟前 via Android
这个不是有好久了吗?
zongren 10
zongren 5 小时 39 分钟前 ❤️ 2
我艹,微信居然有了,前几天刚吐槽过,扫个码贼费劲

JeffGe 11
JeffGe 5 小时 30 分钟前 via Android
Google Play 版的微信还没有
DOLLOR 12
DOLLOR 5 小时 29 分钟前 via Android
支付宝很久以前就有 Shortcut,付款比微信快捷,所以我都是用支付宝来支付。
azkaban 13
azkaban 5 小时 27 分钟前
@wolfan 微信,qq,钉钉这几个狗贼一直没有啊,尤其钉钉,经常扫码,还不能放到桌面快捷方式,得点进去扫
secretman 14
secretman 5 小时 25 分钟前
@azkaban 不这样搞,使用时长的 KPI 完成不了啊
honjow 15
honjow 5 小时 20 分钟前
@wolfan 你确定这功能微信有好久了?
twocold0451 16
twocold0451 5 小时 18 分钟前
微信居然有了,但是没找到在哪编辑
also24 17
also24 5 小时 11 分钟前
上个月还在吐槽只有微信不支持,终于加上这功能了啊……
yaocai321 18
yaocai321 4 小时 52 分钟前
微信牛逼。 手动狗头
Macv1994 19
Macv1994 4 小时 44 分钟前
好多软件好像很早就有这个功能了 但是我一般用到这个功能的时候都是卸载的时候
rodrick 20
rodrick 4 小时 39 分钟前
魅族目前好像没有
lxfcool 21
lxfcool 4 小时 38 分钟前
北京申奥成功辣(狗头
Building 22
Building 4 小时 30 分钟前 via iPhone
iOS 按出菜单后可以直接滑动选择,Android 按出菜单后不抬起来滑一下会直接触发编辑桌面…
bankroft 23
bankroft 4 小时 28 分钟前
@JeffGe #11 gp 版还在 7.0?
yylzcom 24
yylzcom 4 小时 18 分钟前
Google Play 的还在 7.0.21 版本,上次更新还是 12 月 24 日

@Building #22 各个 ROM 的行为不一样吧, 魔趣和 LinegeOS 直接就弹出编辑该应用的菜单, 不会触发编辑桌面的行为
dingwen07 25
dingwen07 4 小时 5 分钟前 via Android
@66beta 长按通知是 OneUI 的功能吧
constexpr 26
constexpr 4 小时 0 分钟前 via Android
扫描界面按返回仍停留在微信界面,?哥不够细
rodneya 27
rodneya 3 小时 59 分钟前
从来没用过这个功能。。。
ronman 28
ronman 3 小时 55 分钟前 via Android
@lxfcool 这种功能本身是很早就有了,但是微信就是*近几天刚刚适配
ronman 29
ronman 3 小时 55 分钟前 via Android
@yylzcom play 版昨天推送了更新,但是版本号依然是 7.0.21 ,无可感知的变化
Delbert 30
Delbert 3 小时 32 分钟前 ❤️ 1
@lxfcool 微信上个版本没有,但是北京申奥成功确是 20 年了。

@Livid 这个算是不友好回复了吧
Cusmate 31
Cusmate 3 小时 5 分钟前 via Android
我记得去年有个版本可以长按弹出扫码的快捷方式,后来又取消了
Cusmate 32
Cusmate 3 小时 4 分钟前 via Android
不记得了到底什么版本,是 2019 年的时候
imn1 33
imn1 2 小时 54 分钟前
@DOLLOR #12
微信也很久以前就有快捷进入扫一扫的方式,点开扫一扫,镜头打开,右上角三点,就出现“添加到桌面”的选项
MIUI v11 *新版,顶栏的扫一扫功能开关支持识别微信,不过我还没试过,因为从桌面快捷进入更快一些
66beta 34
66beta 2 小时 40 分钟前
@dingwen07 一加 氢 11
Tink 35
Tink 2 小时 30 分钟前 via Android
play 版不支持
DOLLOR 36
DOLLOR 2 小时 28 分钟前
@imn1
扫码的话,两家都有快捷图标,差不多。但是微信打开付款码就麻烦了,没有快捷图标,还得进入界面还得点点点好几步才出现。
imn1 37
imn1 2 小时 17 分钟前
@DOLLOR #36
这个确实,微信付款码没有快捷,想快挺麻烦的,不过我自己加了手势,想快也不行
Cielsky 38
Cielsky 2 小时 9 分钟前 via Android
是的好像是 8.0.1 适配了
Jooooooooo 39
Jooooooooo 1 小时 33 分钟前
苹果引领潮流.
wangxiaoaer 40
wangxiaoaer 1 小时 2 分钟前
@Jooooooooo #39 我印象中 Android 的长按图标菜单可比什么 3D Touch 早多了,也好用多了。
phpcxy 41
phpcxy 1 小时 1 分钟前
应该是微信支持了吧,挺方便的。
honjow 42
honjow 37 分钟前
@wangxiaoaer Shortcuts 是 android7.1 才加入的功能。然后查到的 7.1 正式版 2016 年 12 月 5 日推送。iOS 的 3dtouch 菜单*早搭载于 iPhone6s 的 iOS9 系统,6s 和 iOS9 都是 2015 年 9 月发布的
honjow 43
honjow 35 分钟前
@lxfcool 申奥成功和微信刚刚增加 Shortcuts 功能有什么关联吗
Vveeb 44
Vveeb 28 分钟前
@Building 经常调出 iOS 的“右键菜单”滑来滑去,感受下马达震动来解压
tankren 45
tankren 24 分钟前
Play 版本还是 7.0.21.。。
DK7S 46
DK7S 23 分钟前
终于有了吗,之前就烦微信少了这个,那现在可以直接把扫一扫,付款码放桌面了