0%

爬虫(Web Crawler)

郑重声明:本文仅限于编程学习,用于非法目的及造成侵权后果的行为,老子概不负责

http请求

私以为,爬虫程序就是以程序执行代替人工操作,在一定范围的网络资源中找自己要的东西。当人做这项枯燥的工作时,无非输入网址—打开网页—用肉眼识别—下载这样子,下面基本上就是用python模拟这个过程。

1
2
3
4
5
6
7
8
9
10
import requests
import re
response = requests.get('https://qqstone.github.io/qqsnote/2019/10/28/MySQL/')
print(response.text)
if response.text.find('主键'):
print('find it!')
keyUnicode = str('主键'.encode('unicode_escape')).replace('\\\\','\\')[2:14]
print('\S*'+keyUnicode+'\S*')
matchObj = re.findall('\S*'+keyUnicode+'\S*',response.text)
print(matchObj)

反“反爬设置”

有时请求一个网页时,发现无论通过Get或Post以及其他请求方式,都会出现403错误。这种现象多数是由于服务器拒绝了访问,因为这些网页为了防止恶意采集信息,所用的反爬虫设置。

此时可以通过模拟浏览器的header信息来进行访问。

1
2
3
4
5
6
import requests
import re
url='https://www.acfun.cn/a/ac12293064'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36'}
response = requests.get(url,headers=headers)
print(response.text)

上述只是针对user agent检测的手段,通常网站管理员采取的反爬虫基本手段还有封锁IP,检测请求间隔,封锁cookie等,针对这些手段需要采取相应的措施如使用代理,使用sleep模拟点击控制间隔,禁用cookie等

超时处理

使用代理

解析html树

Scrapy框架

依赖:

1
2
3
4
5
pip install Twisted
# windows平台用下面这个
pip install Twisted[windows_platform]

pip install Scrapy

参考 Scrapy文档
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
import scrapy
import re


class QuotesSpider1(scrapy.Spider):
name = "quotes1"
keyword = ""
pages = 75

def start_requests(self):
url = 'https://xxxxxxx.xxx.html'
yield scrapy.Request(url=url, callback=self.parse)
for i in range(2, self.pages):
surl = url.replace('.html', '-'+str(i)+'.html')
yield scrapy.Request(url=surl, callback=self.parse_key_word)

def parse(self, response):
regexp = r'\d{2}'
video_list_tags = response.css('h3::attr(title)')
for vt in video_list_tags:
#if vt.extract().find(self.keyword) >= 0:
match_array = re.findall(regexp, vt.extract())

if len(match_array):
self.log(vt.extract())
filename = 'output.log'
with open(filename, 'a', encoding='utf-8') as f:
f.write(vt.extract()+'-->'+response.url+"\n")

def parse_key_word(self, response):
video_list_tags = response.css('h3::attr(title)')
for vt in video_list_tags:
if vt.extract().find(self.keyword) >= 0:
self.log(vt.extract())
filename = 'output.log'
with open(filename, 'a', encoding='utf-8') as f:
f.write(vt.extract() + '-->' + response.url + "\n")


执行scrapy实例
1
scrapy crawl quotes1

概念

参考:Android 上网概述

使用3g/4g/5g网络的移动设备,通过ppp协议与蜂窝网络的基站建立通信

基带和蜂窝网络
ppp协议是链路层协议

协议结构
Linux对PPP数据链路的建立过程进行抽象,实现了pppd拨号应用程序,专门用于管理PPP数据链路的建立与关闭,见下图。

pppd是一个后台服务进程(daemon),实现了所有鉴权、压缩/解压和加密/解密等扩展功能的控制协议,负责把要发送的数据包传递给PPP协议处理模块,设置PPP协议的参数,用来建立/关闭连接。

Android设备网络访问架构

链路建立的大致过程:
链路建立

( Raspberry Pi Model B Plus Rev1.2 )

setup

官方指引:Setting up your Raspberry Pi

常见异常:HDMI无响应

修改/boot/config.txt

1
2
3
4
5
6
7
hdmi_force_hotplug=1
config_hdmi_boost=4
hdmi_group=2
hdmi_mode=9
hdmi_drive=2
hdmi_ignore_edid=0xa5000080
disable_overscan=1

设置默认命令行启动

1
sudo raspi-config

Boot Options -> Desktop / CLI -> Console
开启ssh server
ssh笔记

GPIO

USB摄像头

Using a standard USB webcam
实践发现,手上的UVC Camera一旦停止调用就会跳出(中断)

搭建直播流服务

4g上网

实践时 电脑USB接口向树莓派供电 插上网卡就电压不足(Under-voltage detected)

lsusb命令识别出” ID 05c6:6000 Qualcomm,Inc.Siemens SG75 “

安装ppp包

内网穿透

ipv6

关于使用ubuntu发行版和centos发行版的倾向(存目)

简单的ubuntu

镜像

实践日期:2019-12-29 ubuntu-18.04.3-live-server-amd64.iso

安装和分区(存目)

安装ssl

配置网卡

不知道怎么就安装了cloud image

参考Configure Ubuntu Server 18.04 to use a static IP address

在线安装Applications

配置apache

启用端口 /etc/apache2/ports.conf

配置/etc/sites-availiable/yoursite.conf
栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<VirtualHost *:90>
ServerName www.example.com

DirectoryIndex index.html
DocumentRoot /home/qqs/Workspace/csc_simulate/
<Directory /home/qqs/Workspace/csc_simulate>
Require all granted
</Directory>
Alias /viewer /home/qqs/Workspace/cs_meshviewer/build
<Directory /home/qqs/Workspace/cs_meshviewer/build>
Require all granted
</Directory>

<Location /viewer>
DirectoryIndex index.html
</Location>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

ProxyPreserveHost On
ProxyPass /download http://10.196.98.58:3000/download
ProxyPassReverse /download http://10.196.98.58:3000/download
</VirtualHost>

注意 并不是唯一的,应根据需要定义路径的访问控制,Difference between Directory and Location
启用和禁用site
1
2
a2ensite yoursite.conf
a2dissite yoursite.conf

启用和禁用模块

1
2
3
4
5
6
7
8
// 关于反向代理
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod lbmethod_byrequests
// 关于虚拟路径
a2enmod alias
// 禁用:a2dismod

启用、停用和重启服务
1
systemctl start|stop|reload apache2

Issue 403 You don’t have permission to access this resource.

令人发狂的CentOS(Selinux)

镜像

实践日期:2019-12-29 CentOS-8-x86_64-1905-dvd1.iso

网卡设置

1
2
3
4
5
6
ip add
nmcli device show
nmcli device status
nmcli connection down enp0s3
nmcli connection up enp0s3
nmcli c reload

静态IP

/etc/sysconfig/network-scripts/ifcfg-enp0s3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=none # 默认是dhcp,根据dhcp分配 改为none或static
NAME=enp0s3
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=enp0s3
UUID=0b813e18-6008-485c-ba8c-d19e259a847a
DEVICE=enp0s3
ONBOOT=yes # 开机启用该配置
IPADDR=10.196.98.99
GATEWAY=10.196.98.1
NETMASK=255.255.254.0
DNS1=10.192.0.100
PREFIX=24

防火墙

1
2
3
4
5
systemctl stop firewalld.service #关闭
systemctl start firewalld.service #启动

firewall-cmd --zone=public --permanent --add-port 22/tcp #开启端口
systemctl restart firewalld.service #重启

关闭selinux

1
2
sestatus // 查看状态
setenforce 0 // 临时关闭

禁用selinux

编辑/etc/selinux/config, set SELINUX=disabled。

概念

存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

  • 存储过程可封装,并隐藏复杂的商业逻辑。

    当dba与后台开发分离时,代码将以call xStoredProcedure 的形式访问数据库
  • 存储过程可以回传值,并可以接受参数。
  • 存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。

    使用存储过程 or 使用 DAO SQL

    Stored Procedure|DAO SQL
    :——-|:——
    数据操作与底层业务解耦,以提供多方使用|不同服务做类似查询需要各自实现
    版本控制与代码分离|一致地版本控制
    仅需开发select和execuate权限|底层应用掌握较高地数据库权限
    增加了数据库运算压力|
    无法应对分表等业务扩展|扩展灵活
    数据库层面拼接查询或无法使用到索引|
    法国人说,使用存储过程封装数据操作主要是因为第一条地目的,然而私以为解耦数据操作和业务的话添加基础服务或中间件更好一些

    语法

    0 语句结构

    修改语句结束符,从分号 ; 临时改为两个 $$,使得过程体中使用的分号被直接传递到服务器,而不会被客户端(如mysql)解释。
    1
    2
    3
    DELIMITER $$

    DELIMITER //
    过程体,类似其他变成语言中{语句块}
    1
    BEGIN .... END    

    1 变量

    1
    2
    3
    4
    5
    -- 存储过程变量,只能在存储过程定义中使用
    DECLARE l_int int unsigned default 4000000;
    -- 用户变量,可以在会话任意位置使用
    SET @p_in=1
    SELECT 'Hello World' into @x;
    赋值多个变量
    1
    select col1, col2, col3 into a, b, c from t limit 1;

    2 函数

    1

    3 入参出参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE PROCEDURE 存储过程名([[IN |OUT |INOUT ] 参数名 数据类形...])

    set @p_in=1;
    -- 假设STOREDPROCEDURE1是带一个入参的存储过程
    call STOREDPROCEDURE1(@p_in);
    -- 入参可以是字面量
    call STOREDPROCEDURE1(1);


    条件分支

    1
    2
    3
    4
    5
    if a>b then
    select a;
    else
    select b;
    end if;
    1
    2
    3
    4
    5
    6
    7
    8
    case type
    when 0 then
    insert into t values(101);
    when 1 then
    insert into t values(11);
    else
    insert into t values(1);
    end case

    循环

    1
    2
    3
    while i<10 do
    insert into t values(i)
    end while
    1
    2
    3
    4
    5
    6
    7
    8
    set v=0;  
    LOOP_LABLE:loop
    insert into t values(v);
    set v=v+1;
    if v >=5 then
    leave LOOP_LABLE;
    end if;
    end loop;

    游标

    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
    create procedure asset_owner_clean()
    begin
    declare _id int; -- asset list primary key
    declare targetid int; -- target user employee ID
    declare asset_owner varchar(80);
    declare asset_ownerid int;
    declare done int default false;
    -- set cursor traverse asset list whose owner is not null
    declare asset cursor for
    (select asset_id,owner,owner_id from all_asset where
    (owner is not null and owner <>'') or (owner_id is not null and owner_id <>''));

    declare continue handler for not found set done = true;
    open asset;
    checkloop: loop
    fetch asset into _id,asset_owner,asset_ownerid;
    SELECT _id, asset_owner, asset_ownerid;
    if done then
    leave checkloop;
    end if;
    -- loop body start
    <insert statements>
    -- loop body end
    end loop checkloop;
    close asset;
    commit;
    end
    注意:跳出循环的句柄是not found,意味着循环体中将empty set赋值给变量会跳出loop

    如 selete * into _list where 1=2;

SQL Server中调用存储过程的语法稍有不同, 见文章TransactSQL

  • https本身没有端口限制
  • https支持传输ssl证书以避免服务端仿冒
  • https报文使用对称加密 密钥包含在证书中

postman的”验证ssl证书”开关: settings -> General -> SSL certificate verification

ssl certificate warning

ssl warning

  • SSL证书不是来自公认的证书颁发机构(CA)
  • 数字证书信任链配置错误
  • 证书的域名匹配程度不完整
  • 证书已经过了有效期
  • 客户端不支持SNI协议
    由于第一条,开发环境的带自签名的https服务仅被本地认证,远程访问就会出现warning

生成自签名证书

信任证书

实现局域网访问HTTPS

数据分为持久化和非持久化两类。对于容器,非持久化数据从属其容器,生命周期与容器相同,删除容器时也会删除所有非持久化数据。如果希望自己的容器数据保存下来,需要将数据存储在卷上,卷与容器是解耦的,可以独立地创建并管理。

在主机上创建Docker的卷,实质上是创建一个目录,使之能且只能被Docker进程修改,除了‘卷’,可使用绑定装载的方式在主机和容器间共享文件

卷命令

1
2
3
4
5
docker volume create myvol
docker volume ls // 查看已创建地卷
docker volume inspect myvol // 查看指定卷详细信息
docker volume rm myvol // 删除指定卷
docker volume prune // 删除所有未装入容器使用的卷

装入容器

-v, —volume

1
docker run -d --name myjenkins -p 8080:8080 -p 50000:50000 -v /var/jenkins_home:/var/jenkins_home --restart always jenkins

用:分隔的3个field,卷名称 : 路径或文件 : 选项,通常只保留卷路径

ro 即 readonly

原文:

In the case of named volumes, the first field is the name of the volume, and is unique on a given host machine. For anonymous volumes, the first field is omitted.

The second field is the path where the file or directory will be mounted in the container.

The third field is optional, and is a comma-separated list of options, such as ro.

上例中,-v /var/jenkins_home:/var/jenkins_home 两个field前者是宿主路径,后者是容器路径,即将jenkins用户数据保存到容器的/var/jenkins_home同时持久化到本地位于/var/jenkins_home的卷中

引述 jenkins官网

-v $HOME/jenkins:/var/jenkins_home would map the container’s /var/jenkins_home directory to the jenkins subdirectory within the $HOME directory on your local machine, which would typically be /Users//jenkins or /home//jenkins.

—mount

1
2
docker run --name myjenkins -p 8080:8080 -p 50000:50000 \ 
--mount source=jenkins_home,target=/var/jenkins_home,driver=local --restart always jenkins

用 , 分隔三个字段,卷名称,目标路径或文件,驱动。