静觅丨崔庆才的个人站点

而Pyppeteer和Selenium就是用的第三种方法,下面我们再用Pyppeteer来试试,如果用Pyppeteer实现如上页面的抓取的话,代码就可以写为如下形式:

好了,知道这些参数之后,我们可以先试试看。首先可以试用下最常用的参数headless,如果我们将它设置为True或者默认不设置它,在启动的时候我们是看不到任何界面的,如果把它设置为False,那么在启动的时候就可以看到界面了,一般我们在调试的时候会把它设置为False,在生产环境上就可以设置为True,我们先尝试一下关闭headless模式:

公众号”进击的Coder”回复”Pyppeteer”即可获取本节全部代码。

【顺手提供】精通正则表达式:第三版PDF(高清-中文-带标签)

1logging.Formatter.__init__(fmt=None,datefmt=None,style='%')该构造方法接收3个可选参数:

FilterFilter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。该类定义如下:

12classlogging.Filter(name='')filter(record)比如,一个filter实例化时传递的name参数值为’A.B’,那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:’A.B’,’A.B,C’,’A.B.C.D’,’A.B.D’,而名称为’A.BB’,‘B.A.B’的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。

排序结果就是该十进制数的二进制表示。例如十进制数101转换为二进制数的计算过程如下:

现在,我们已经了解到二进制与十进制的换算方法,并拥有了进制对照表。但在开始学习位运算符之前,我们还需要了解补码的知识。数值有正负之分,那么仅有0和1的二进制如何表示正负呢?人们设定,二进制中最高位为0代表正,为1则代表负。例如00001100对应的十进制为12,而10001100对应的十进制为\-12。这种表示被称作原码。但新的问题出现了,原本二进制的最高位始终为0,为了表示正负又多出了1,在执行运算时就会出错。举个例子,1+(-2)的二进制运算如下:

12300000001+10000010=10000011=-3这显然是有问题的,问题就处在这个代表正负的最高位。接着,人们又弄出了反码(二进制各位置的0与1互换,例如00001100的反码为11110011)。此时,运算就会变成这样:

1234500000001+11111101=11111110#在转换成十进制前,需要再次反码=10000001=-1这次好像正确了。但它仍然有例外,我们来看一下1+(-1):

123400000001+1111+1110=11111111=10000000=-0零是没有正负之分的,为了解决这个问题,就搞出了补码的概念。补码是为了让负数变成能够加的正数,所以负数的补码=负数的绝对值取反+1,例如\-1的补码为:

123456#1的补码+-6的补码00000001+11111010=11111011#补码运算结果=11111010#对补码减1,得到反码=10000101#反码取反,得到原码=-5#对应的十进制123456#1的补码+-9的补码00000001+11110111=11111000#补码运算结果=11110111#对补码减1,得到反码=10001000#反码取反,得到原码=-8#对应的十进制要注意的是,正数的补码与原码相同,不需要额外运算。也可以说,补码的出现就是为了解决负数运算时的符号问题。

位运算分为6种,它们是:

名称

符号

按位与

&

按位或

|

按位异或

^

按位取反

~

左移运算

<<

右移运算

>>

按位与运算将参与运算的两数对应的二进制位相与,当对应的二进制位均为1时,结果位为1,否则结果位为0。按位与运算的运算符为&,参与运算的数以补码方式出现。举个例子,将数字5和数字8进行按位与运算,其实是将数字5对应的二进制00000101和数字8对应的二进制00001000进行按位与运算,即:

12300000101&00001000根据按位与的规则,将各个位置的数进行比对。运算过程如下:

按位或运算将参与运算的两数对应的二进制位相或,只要对应的二进制位中有1,结果位为1,否则结果位为0。按位或运算的运算符为|,参与运算的数以补码方式出现。举个例子,将数字3和数字7进行按位或运算,其实是将数字3对应的二进制00000011和数字7对应的二进制00000111进行按位或运算,即:

12300000011|00000111根据按位或的规则,将各个位置的数进行比对。运算过程如下:

1234500000011|00000111--------00000111最终得到的结果为00000111。将结果换算成十进制,得到7,即3|7=7。

按位异或运算将参与运算的两数对应的二进制位相异或,当对应的二进制位值不同时,结果位为1,否则结果位为0。按位异或的运算符为^,参与运算的数以补码方式出现。举个例子,将数字12和数字7进行按位异或运算,其实是将数字12对应的二进制00001100和数字7对应的二进制00000111进行按位异或运算,即:

12300001100^00000111根据按位异或的规则,将各个位置的数进行比对。运算过程如下:

1234500001100^00000111--------00001011最终得到的结果为00001011。将结果换算成十进制,得到11,即12^7=11。

按位取反运算将二进制数的每一个位上面的0换成1,1换成0。按位取反的运算符为~,参与运算的数以补码方式出现。举个例子,对数字9进行按位取反运算,其实是将数字9对应的二进制00001001进行按位取反运算,即:

1234~00001001=00001001#补码,正数补码即原码=11111010#取反=-10最终得到的结果为\-10。再来看一个例子,\-20按位取反的过程如下:

1234~00010100=11101100#补码=00010011#取反=19最终得到的结果为19。我们从示例中找到了规律,按位取反的结果用数学公式表示:我们可以将其套用在9和\-20上:

12~9=-(9+1)=-10~(-20)=-((-20)+1)=19这个规律也可以作用于数字0上,即~0=-(0+1)=-1。

左移运算将数对应的二进位全部向左移动若干位,高位丢弃,低位补0。左移运算的运算符为<<。举个例子,将数字5左移4位,其实是将数字5对应的二进制00000101中的二进位向左移动4位,即:

右移运算将数对应的二进位全部向右移动若干位。对于左边的空位,如果是正数则补0,负数可能补0或1(TurboC和很多编译器选择补1)。右移运算的运算符为\>>。举个例子,将数字80右移4位,其实是将数字80对应的二进制01010000中的二进位向右移动4位,即:

123480>>4=01010000>>4=00000101#正数补0,负数补1=5最终结果为5。这等效于:也就是说,右移运算的规律为:要注意的是,不能整除时,取整数。这中除法取整的规则类似于PYTHON语言中的地板除。

12345#pythonif101%2:print('偶数')else:print('奇数')我们也可以通过位运算中的按位与来实现奇偶判断,例如:

12345#pythonif101&1:print('奇数')else:print('偶数')这是因为奇数的二进制最低位始终为1,而偶数的二进制最低为始终为0。所以,无论任何奇数与1即00000001相与得到的都是1,任何偶数与其相与得到的都是0。变量交换在C语言中,两个变量的交换必须通过第三个变量来实现。伪代码如下:

1234567#伪代码a=3,b=5c=aa=bb=a--------a=5,b=3在PYTHON语言中并没有这么麻烦,可以直接交换。对应的PYTHON代码如下:

1234#pythona,b=3,5a,b=b,aprint(a,b)代码运行结果为53。但大部分编程语言都不支持PYTHON这种写法,在这种情况下我们可以通过位运算中的按位异或来实现变量的交换。对应的伪代码如下:

12345#伪代码a=3,b=5a=a^bb=a^ba=a^b最后,a=5,b=3。我们可以用C语言和PYTHON语言进行验证,对应的PYTHON代码如下:

123456#pythona,b=3,5a=a^bb=a^ba=a^bprint(a,b)代码运行结果为53,说明变量交换成功。对应的C代码如下:

12交换前:a=3,b=5交换后:a=5,b=3这说明变量交换成功。求x与2的n次方乘积设一个数为x,求x与2的n次方乘积。这用数学来计算都是非常简单的:在位运算中,要实现这个需求只需要用到左移运算,即x<>k&1。我们可以用PYTHON代码进行验证:

1234#pythonx=5#00000101foriinrange(8):print(x>>i&1)代码运行结果如下:

1234567810100000这说明位运算的算法是正确的,可以满足我们的需求。判断赋值

1234ifa==x:x=belse:x=a等效于x=a^b^x。我们可以通过PYTHON代码来验证:

1234567#pythona,b,x=6,9,6ifa==x:x=belse:x=aprint(a,b,x)代码运行结果为699,与之等效的代码如下:

上周五,大师兄发给我一个网址,哭哭啼啼地求我:“去!把这个网页上所有年所有县所有作物的数据全爬下来,存到Access里!”我看他可怜,勉为其难地挥挥手说:“好嘞,马上就开始!”

1234response1=requests.get(url,headers=headers)ifresponse1.status_code==200:cookies=response1.cookiesprint(cookies)输出:

1,]>Nah,看不懂,不看不管,直接把它放到post里试试

现在既然已经拿到了目标页面的HTML,那在获取所有年、地区、州名、县名之前,先测试一下pandas.read_html提取网页表格的功能。pandas.read_html这个函数时在写代码时IDE自动补全下拉列表里瞄到的,一直想试试来着,今天乘机拉出来溜溜:

123456789101112remain=[[str(year),str(region),str(state),str(county)]foryearinyearsforregioninregionsforstateinregion_state[region]forcountyinstate_county[state]]remain=pd.DataFrame(remain,columns=['CRMSearchForm[year]','CRMSearchForm[region]','CRMSearchForm[state]','CRMSearchForm[county]'])remain.to_csv('remain.csv',index=False)#由于州名有缩写和全称,也本地保存一份importjsonwithopen('region_state.json','w')asjson_file:json.dump(region_state,json_file,indent=4)我看了一下,一共49473行——也就是说至少要发送49473个post请求才能爬完全部数据,纯手工获取的话大概要点击十倍这个数字的次数……

那么开始爬咯

周一,我把跑出来的数据发给大师兄,大师兄回我:“好的”。隔着屏幕我都能感受到滔滔不绝的敬仰和感激之情,一直到现在,大师兄都感动地说不出话来。

要使用Axios,你需要先安装它。

1npminstallaxios下面是一个演示Axios行动的基本例子。

输入命令查看JDK安装路径:/usr/libexec/java_home-V

121.8.0_60,x86_64:"JavaSE8"/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home需要把上面的路径配置到环境变量中,ANDROID_HOME就是AndroidSDK的安装路径。

输入命令打开配置文件:open~/.bash_profile,在文件中添加如下内容:

123exportJAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/HomeexportPATH=$JAVA_HOME/bin:$PATHexportANDROID_HOME=/Users/chenwenguan/Library/Android/sdk输入命令让配置立即生效:source~/.bash_profile

在首次使用Appium时可能会出现一个错误:

1CouldnotdetectMacOSXVersionfromsw_versoutput:'10.13.2在终端输入命令:

1grep-rl"CouldnotdetectMacOSXVersionfromsw_versoutput"/Applications/Appium.app/得到如下结果:

在AndroidStudio打开Monitor工具:Tools->Android->AndroidDeviceMonitor按照下图的步骤查看控件的ID等属性,后续在代码实现中会用到。

AndroidDeviceMonitor

chromeinspect页面

Chromeinspect查看WebView详细内容

设备信息

AppiumURL参数设置

不管是WebView还是Android原生ListView的滑动都需要在Android原生上下文环境下操作driver.context(“NATIVE_APP”);滑动操作都可以通过如下代码实现,通过滑动前后的PageSource对比可以知道列表是否已经滑动到底部。

1driver.findElement(By.xpath("//android.widget.ImageView[@content-desc='返回']")).click();时候经常出现如下错误,改为

1driver.findElementById("com.tencent.mm:id/ht").click();异常消失,猜测原因就是因为By.xpath()方法查找比较耗时导致。

自动化脚本程序要跑起来需要两个压缩包,java-client-3.1.0.jar和selenium-server-standalone-2.44.0.jar,试过使用这两个JAR包的最新版本,会有一些奇奇怪怪的问题,这两个版本的JAR包够用了。java-client-3.1.0.jar可以从Appium官网下载:

123java-client-3.1.0.jarselenium-server-standalone-2.44.0.jarAppiumWeChatAuto/appiumauto/src/main/java/com/example/AppiumAutoScan.javaEclipseIDE下载地址:

那么前端完成一个合格的验证码,究竟需要做成什么样子呢?

以上就是验证码的两个基本要求,所以我们这里就来实现一下看看。

下面就具体讲解下这个是怎么实现的,实际上核心代码只有200行,下面对整个核心流程进行说明。既然Vue这么火,那我这里就用Vue来实现啦,具体的环境配置这里就不再赘述了,需要安装的有:

安装完成之后便可以使用vue命令了,新建个项目:

对于Drop组件来说,它是一个被放置的对象,被拖动滑块会放到这个Drop滑块上,这就代表拖动成功了。它有两个主要的事件需要监听,一个叫做dragover,一个叫做dragleave,分别用来监听Drag对象拖上和拖开的事件。在这里,分别对两个事件设置了onDragOver和onDragLeave的回调函数,当Drag对象放到Drop对象上面的时候,就会触发onDragOver对象,当拖开的时候就会触发onDragLeave事件。那这样的话我们只需要一个全局变量来记录是否已经将滑块拖动到目标位置即可,比如可以定一个全局变量state,我们用over属性来代表是否拖动到目标位置。因此onDragOver和onDragLeave事件可以这么实现:

1

这部分定义了在拖动过程中随鼠标移动的图片样式,这里也和Drag滑块一样定义了一样的样式,这样在拖动的过程中,就会显示一个和Drag滑块一样的滑块随鼠标移动。最后,就是拖拽完成之后,将滑动轨迹输出出来,这里我就直接呈现在页面上了,