阿C的博客

Category: Python

本文主题-规避反爬虫 什么是反爬虫?参看 # 反爬虫(Anti-spider) #

今天穿插一个话题,教大家怎么规避反爬虫。一般来说我们会遇到网站反爬虫策略主要有下面几点:

  1. 限制IP访问频率,超过频率就断开连接。
  2. 限制UA访问频率,超过频率就断开连接。[介绍]
  3. 限制Session访问频率,超过频率就断开连接。
  4. 限制Cookies访问频率,超过频率就断开连接。
附加说明:
针对IP: 爬虫解决办法是,降低爬虫的速度在每个请求前面加上 sleep() 或者不停的更换代理IP。
针对UA: 爬虫解决办法是,准备大量UserAgent随机调用。

我们今天就来针对1、2两点来写出反爬虫的下载模块,别害怕真的很简单。

使用模块
  • requests(网络请求模块)
  • re(正则表达式模块)
  • random(随机选择模块)
实现思路
  1. 代理IP发布网站中获取有效IP地址;
  2. 本地IP访问网页;当本地IP失效时,转换使用代理IP访问网页;
  3. 代理IP失败六次后转换下一个代理IP。

下面我们开整ヽ(●-`Д´-)ノ


第一步、先看看Python的默认UA
import requests

payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=payload)
print r.text

运行结果:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "key1": "value1",
    "key2": "value2"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "close",
    "Content-Length": "23",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.13.0"
  },
  "json": null,
  "origin": "220.200.59.163",
  "url": "http://httpbin.org/post"
}

我们可以看到,程序请求页面的UA是这样的: python-requests/2.13.0,而正常浏览器请求页面的UA应该是这样的:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0

规避反爬虫-正常UA

反爬虫程序若做上述检测,则当场被抓现形,直接枪毙。

所以呢,我们必须要伪造正常浏览器访问的UA,同时为了一次性解决 2.限制UA访问频率 ,我们还需要伪造大量的UA,请求时随机选取一个,混淆视听。

Continue reading

爬虫第一炮-福利网站:妹子图

基础环境

  • python环境:2.7.13
  • python类库:Requests[1]、beautifulsoup[2]、LXML[3]
pip install Requests
pip install beautifulsoup4
pip install LXML

流程示例图

爬虫教程第一炮之流程示意图

  • 爬虫入口:顾名思义我们需要程序从什么地方开始爬取网页
  • 存储数据:如果获取的网页有你需要的内容则取出数据保存
  • 提取页面URL:如果你你获取到的网页没有你需要的数据,但是有前往该数据页面的地址URL,则获取该地址URL,再次循环爬虫入口爬取

准备工作完了 ヽ(●-`Д´-)ノ ,我们马上开搞码代码了。

一个简单爬虫的诞生大慨需要下面几个步骤(图很简陋、将就着看看)。


第一步、获取顶级爬虫入口

好啦!现在来开始看看网站找一个爬虫入口(开始爬取的页面):http://www.mzitu.com/all/

爬虫教程第一炮之入口页面

Continue reading

Argparse教程

本教程简明地介绍了Python标准库推荐使用的命令行参数解析模块 —— Argparse的使用。这里Argparse的编写环境为Python 3,一些细节有别于2.x版本,特别是一些异常消息,在3.x版本中有所改进。

注意: 有其他两个模块,也完成同样的任务。即 getopt (一个等价于C语言的getopt()) 和 弃用的 optparse。另外,Argparse 是基于 optparse,因此在使用方面非常相似。

概念

在本入门教程中,让我们通过使用ls命令来展示Argparse的功能:

$ ls
cpython  devguide  prog.py  pypy  rm-unused-function.patch
$ ls pypy
ctypes_configure  demo  dotviewer  include  lib_pypy  lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x  4 wena wena 4096 Feb  8 12:04 devguide
-rwxr-xr-x  1 wena wena  535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb  7 00:59 pypy
-rw-r--r--  1 wena wena  741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...

从以上的四个命令中,我们能够了解以下几个基本概念:

  • 命令一: ls命令在没有任何参数的情况下也是可以运行的,默认显示当前目录的内容。
  • 命令二: 如果我们想要让它显示更多内容,那么我们需要给它多一点参数。比如,我们想显示一个不同的目录,pypy。我们所做的就是明确指定位置参数(Positional Argument)。这样程序就会根据命令行中的参数得知它所在的位置并进行显示。这个概念更像cp这样的命令,它的基本用法是 cp SRC DEST ,SRC表示的是您想要拷贝的文件,DEST表示你想要将文件拷贝到哪里。
  • 命令三: 现在,我们想要改变程序的行为。在我们的示例中,我们想为每个文件显示更多的信息,而不仅仅是文件名。在这种情况下,-l被称为可选参数(Optinal Argument)
  • 命令四: 最后是显示帮助的文档的一个片段。这是非常有用的,当你遇到你从未使用过的命令时,你可以通过它学习怎么使用。
基本认识

让我们先从一个非常简单的例子开始(它什么也不做):

import argparse
parser = argparse.ArgumentParser()
parser.parse_args()

运行结果:

$ python prog.py
$ python prog.py --help
usage: prog.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo

结果分析:

  • 命令一: 若不给参数而运行脚本,将不会得到任何结果。
  • 命令二: 显示 Argparse 模块的有用之处。我们几乎没有做什么,但我们已经得到一个不错的帮助信息。我们无需人为设置–help-h参数,就能得到一不错的帮助信息。
  • 命令三 & 命令四: 指定其他参数会导致错误。
位置参数

一个示例:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print args.echo

运行结果:

$ python prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python prog.py --help
usage: prog.py [-h] echo

positional arguments:
  echo

optional arguments:
  -h, --help  show this help message and exit
$ python prog.py foo
foo

结果分析:

  • 我们增加了一个 add_argument() 方法,用来指定程序可接受的命令行参数。在这种情况下,我将命名为符合其功能的参数名称 echo
  • 现在要运行程序,就必须指定一个参数。
  • parse_args() 方法实际上从我们的命令行参数中返回一些数据,在上面的例子中是echo。
  • 这个像“魔法”一样的过程,在 Argparse 中是自动完成的。

注意:尽管自动产生的帮助看起来显示不错,目前不是那么有用。例如我们看到 echo 作为一个位置参数,但是我们仍然无法知道它是做什么用的。所以,我们增加了一些东西,使得它变得更加有用:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print args.echo

运行结果:

$ python prog.py -h
usage: prog.py [-h] echo

positional arguments:
  echo        echo the string you use here

optional arguments:
  -h, --help  show this help message and exit

现在,做一些更有用的事情:(计算输入参数 square 的平方)

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print args.square**2

运行结果:

$ python prog.py 4
Traceback (most recent call last):
  File "prog.py", line 5, in <module>
    print args.square**2
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

这个程序不能正确运行,因为 Argparse 会将输入参数当作字符串处理,所以我们需要设置它的类型:(type=int)

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number", type=int)
args = parser.parse_args()
print args.square**2

运行结果:

$ python prog.py 4
16
$ python prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'

进行得很顺利。这个程序现在甚至可以在非法输入时报错退出。

可选参数

迄今为止,我们一直在玩位置参数。让我们看看如何添加可选参数:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
    print "verbosity turned on"

运行结果:

$ python prog.py --verbosity 1
verbosity turned on
$ python prog.py
$ python prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]

optional arguments:
  -h, --help            show this help message and exit
  --verbosity VERBOSITY
                        increase output verbosity
$ python prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument

结果分析:

  • 命令一: 程序在指定 –verbosity 时显示内容。该参数实际上是可选的,没有指定运行程序也不会出现错误。请注意,默认情况下,如果不使用可选参数,则相关变量( args.verbosity )将被赋值 None ,这时 if 语句将判断为失败。另外,帮助消息也会相应更改。
  • 命令二: 需要使用 –verbosity 参数时,必须指定值,值是什么无所谓。

上述示例接受 –verbosity 参数的任意值,但对于当前程序,实际上只需要两个值: TrueFalse 。 我们相应地修改代码:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
   print "verbosity turned on"

运行结果:

$ python prog.py --verbose
verbosity turned on
$ python prog.py --verbose 1
usage: prog.py [-h][--verbose]
prog.py: error: unrecognized arguments: 1
$ python prog.py --help
usage: prog.py [-h][--verbose]

optional arguments:
  -h, --help  show this help message and exit
  --verbose   increase output verbosity

结果分析:

  • 命令一: args.verbose 参数现在是一个标记,而不需要一个具体的值。我们甚至更改了该参数的名称以匹配该想法。请注意,我们现在指定一个新的关键字 action ,并赋值“store_true”。 这意味着,如果指定了该参数,则将赋值 True ,不指定意味着为 False
  • 命令二: 当你为此参数指定值时,它会报错。
  • 其他: 请注意不同的帮助文本。
短参数

如果您熟悉命令行的使用,您会注意到我尚未提及到参数的简短版本的话题。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
    print "verbosity turned on"

运行结果:

$ python prog.py -v
verbosity turned on
$ python prog.py --help
usage: prog.py [-h] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose  increase output verbosity

注意:新功能也反映在帮助文本中。

位置参数和可选参数相结合

我们不断增加程序的复杂度:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true", help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
    print "the square of {} equals {}".format(args.square, answer)
else:
    print answer

运行结果:

$ python prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python prog.py 4
16
$ python prog.py 4 --verbose
the square of 4 equals 16
$ python prog.py --verbose 4
the square of 4 equals 16
  • 命令一: 我们绑定了一个位置参数,因此无参数传入报错。
  • 命令二 & 命令三: 请注意,顺序并不重要。

我们如何给我们这个程序提供多种详细输出(Multiple Verbosity)的格式,请看代码:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print "the square of {} equals {}".format(args.square, answer)
elif args.verbosity == 1:
    print "{}^2 == {}".format(args.square, answer)
else:
    print answer

运行结果:

$ python prog.py 4
16
$ python prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python prog.py 4 -v 1
4^2 == 16
$ python prog.py 4 -v 2
the square of 4 equals 16
$ python prog.py 4 -v 3
16

除了最后一个外,这些都看起来很好,这在我们的程序中暴露了一个错误。 我们通过限制 –verbosity 参数可以接受的值来解决它:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2], help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print "the square of {} equals {}".format(args.square, answer)
elif args.verbosity == 1:
    print "{}^2 == {}".format(args.square, answer)
else:
    print answer

运行结果:

$ python prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square

positional arguments:
  square                display a square of a given number

optional arguments:
  -h, --help            show this help message and exit
  -v {0,1,2}, --verbosity {0,1,2}
                        increase output verbosity

注意:修改代码也会同时反映出错误消息以及帮助文本。

现在,让我们使用一个不同的方法玩 –verbosity ,这是很常见的。它也匹配 CPython 可执行文件处理其自己的verbose参数的方式(检查 python –help 的输出):

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count", help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print "the square of {} equals {}".format(args.square, answer)
elif args.verbosity == 1:
    print "{}^2 == {}".format(args.square, answer)
else:
    print answer

我们引入了另一个动作 “count” 来计算特定可选参数的出现次数:

$ python prog.py 4
16
$ python prog.py 4 -v
4^2 == 16
$ python prog.py 4 -vv
the square of 4 equals 16
$ python prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python prog.py 4 -h
usage: prog.py [-h] [-v] square

positional arguments:
  square           display a square of a given number

optional arguments:
  -h, --help       show this help message and exit
  -v, --verbosity  increase output verbosity
$ python prog.py 4 -vvv
16
  • 命令一 & 命令一 & 命令三: 是的,脚本的以前版本使用的是一个标志(action=”store_true”)。这里解释一下与“计数”的不同。“计数”,就像“store_true”操作一样,如果不指定-v标志,该标志被认为是 None
  • 命令四: 指定参数的长形式,我们也会得到相同的输出。
  • 命令六: 令人遗憾的是,我们的帮助文本对于我们的脚本获得新特性支持的并不十分丰富,但总是可以通过改进脚本的文档(例如通过help关键字)来修复。
  • 命令七: 最后一个输出会在我们的程序中出现一个BUG。

让我们FIX它:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2

# bugfix: replace == with >=
if args.verbosity >= 2:
    print "the square of {} equals {}".format(args.square, answer)
elif args.verbosity >= 1:
    print "{}^2 == {}".format(args.square, answer)
else:
    print answer

运行结果:

$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python prog.py 4
Traceback (most recent call last):
  File "prog.py", line 11, in <module>
    if args.verbosity >= 2:
TypeError: unorderable types: NoneType() >= int()
  • 命令一 & 命令二: 输出顺利,并修复了我们以前的Bug。也就是说,我们希望任何值 >= 2 尽可能冗长。
  • 命令三: 存在问题。

让我们FIX这个BUG:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int, help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
    print "the square of {} equals {}".format(args.square, answer)
elif args.verbosity >= 1:
    print "{}^2 == {}".format(args.square, answer)
else:
    print answer

我们刚刚推出了另一个关键字-default。我们将其设置为0,以使其与其他int值相当。请记住,默认情况下,如果未指定可选参数,将赋值None,并且不能与int值进行比较(因此报出TypeError异常)。

再次运行:

$ python prog.py 4
16

到目前为止,我们已经学到了很多东西,但我们仍只是触及了 Argparse 的表面。模块功能非常强大,在我们结束本教程之前,我们将进一步探讨一下。

更上一层楼

如果我们想扩展我们的小程序来执行其他权力,而不仅仅是求平方:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print "{} to the power {} equals {}".format(args.x, args.y, answer)
elif args.verbosity >= 1:
    print "{}^{} == {}".format(args.x, args.y, answer)
else:
    print answer

运行结果:

$ python prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python prog.py -h
usage: prog.py [-h] [-v] x y

positional arguments:
  x                the base
  y                the exponent

optional arguments:
  -h, --help       show this help message and exit
  -v, --verbosity
$ python prog.py 4 2 -v
4^2 == 16

注意:到目前为止,我们一直在使用详细程度级别(Verbosity Level)来更改显示的文本。以下示例代替使用详细程度级别来显示更多文本:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print "Running '{}'".format(__file__)
if args.verbosity >= 1:
    print "{}^{} ==".format(args.x, args.y),
print answer

运行结果:

$ python prog.py 4 2
16
$ python prog.py 4 2 -v
4^2 == 16
$ python prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16
选项冲突

到目前为止,我们已经使用了两个 argparse.ArgumentParser 实例的方法。 我们来介绍第三个, add_mutually_exclusive_group()。它允许我们指定彼此冲突的参数。我们还要改变程序的其余部分,使新功能更有意义:我们将介绍 –quiet 选项,这将与 –verbose 相反:

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print answer
elif args.verbose:
    print "{} to the power {} equals {}".format(args.x, args.y, answer)
else:
    print "{}^{} == {}".format(args.x, args.y, answer)

我们的程序现在更简单,为了演示,我们已经去掉了一些功能。无论如何,程序运行输出如下:

$ python prog.py 4 2
4^2 == 16
$ python prog.py 4 2 -q
16
$ python prog.py 4 2 -v
4 to the power 2 equals 16
$ python prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose

这种方式应该很容易遵循。最后一个命令,你可以看到程序获得的灵活性,即混合长形式参数与短格式参数结合的情况。

在我们总结之前,您可能想要告诉用户您的程序的主要目的,以防他们不知道:

import argparse

parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print answer
elif args.verbose:
    print "{} to the power {} equals {}".format(args.x, args.y, answer)
else:
    print "{}^{} == {}".format(args.x, args.y, answer)

注意:使用说明略有不同。 注意 [-v | -q] ,它告诉我们,我们可以使用 -v-q ,但不能同时使用两者:

$ python prog.py --help
usage: prog.py [-h] [-v | -q] x y

calculate X to the power of Y

positional arguments:
  x              the base
  y              the exponent

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose
  -q, --quiet
结论

Argparse 模块提供了很多比这里介绍的更多的功能。它的功能文档是非常详细和完全的,有充分的例子。通过本教程,您应该很容易地消化它们,而不会感到不知所措。

原(Ying)文地址:Argparse Tutorial

Copyright © 2020 阿C的博客

Theme by AC.AsiaUp ↑