Python与爬虫基础

python基础

1.Python有6种标准数据类型

  • 数字
  • 布尔(Boolean)
  • 字符串(string)
  • 列表(List)
  • 元组(tuple)
  • 字典(dict)

2.Python支持4种数字类型

  • int(整形)
  • long(长整型,python3取消,变为int)
  • float(浮点型)
  • complex(复数)

3.输入与输出

  • print不换行: print(x, end=" ")
  • input("请输入x值:")
  • python保留字

4.数据结构 List

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
lst = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
print(lst)
print(lst[2])
# 向后添加元素
lst.append('hello')
print(lst)
# 删除最后一个元素
lst.pop()
print(lst)
# 删除内容为'abcd'的元素
lst.remove('abcd')
print(lst)
list_ = [2,3,5,1,4,6]
# 按数字大小进行排序
list_.sort()
print(list_)

#列表的便利
list_ = [1,2,3,4]
for i in list_:
print(i)

i = 0
while i < len(list_):
print(list_[i])
i+=1

元组 tuple,与列表基本相同,区别是使用()而不是[],而且元组是只读的

1
2
3
tp = ( 'abcd', 786 , 2.23, 'john', 70.2  )
print(tp)
print(tp[2])

切片 字符串和列表都存在切片操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
a = "abcdef"
b = ['a','b','c','d','e','f']
#len方法可以计算字符串、列表的长度
len_ = len(a)
print(len_)
# 完整格式
print(a[0:6:1])
print(b[0:6:1])
# 简写格式
print(a[1:4])
print(b[1:4])
# 设置步进,不写默认最前到最后
print(a[::2])
print(b[::2])
# 逆步进(倒序)
print(a[::-1])
print(b[::-1])
# 前五个2步进
print(a[0:5:2])
print(b[0:5:2])
# 前3个后3个
print(a[:-3])
print(a[-3:])

字典 字典是另一种可变容器模型,且可存储任意类型对象。 格式如下所示:d = {key1 : value1, key2 : value2 } 键必须是唯一的,但值则不必。 值可以取任何数据类型,但键必须是不可变数据类型,如字符串,数字或元组。

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
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

# 获取元素
name1 = dict['Name']
# get第二个参数默认是None
name2 = dict.get('Name')
print(name1)
print(name2)
# 添加元素
dict['Address'] = 'beijing'
print(dict)
# 修改元素,直接用键值覆盖
dict['Name'] = 'xiaotian'
print(dict)
# 删除字典元素
del dict['Address']
print(dict)

# 字典的遍历
dict_ = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
# 遍历keys(键)
for i in dict_.keys():
print(i)
print('---------------------------')
# 遍历values(值)
for i in dict_.values():
print(i)
print('---------------------------')
# 遍历items(键、值)
for i in dict_.items():
print(i)
for key,value in dict_.items():
print (key,end='-')
print (value)

函数

在Python之中定义函数要遵循以下几点:

  • python中定义函数使用 def 关键字,后面接着函数名称和括号(),最后跟着冒号
  • 函数的参数应当写在括号之中,也可以在括号中定义参数
  • 函数中的 docstring 是可选的,可写可不写,建议写上
  • 函数中的代码块要进行缩进
  • return 可以用于返回函数返回值或者表达式,return后面什么都不写与 return None 或者不写return 等价

1.参数

调用参数的时候可以使用以下几种参数:

  • 必要(required)参数
  • 关键(keyword)字参数
  • 默认(default)参数
  • 变长(variable-length)参数

必要参数是指必须按照函数定义的参数列表的定义,按照顺序传入,如change_list函数就必须传入mylist参数,如果没有按照要求进行传递参数,将会报错:

1
2
def change_list(mylist):
mylist.append('test')

当使用关键字参数对函数进行调用的时候,要按照函数定义的参数名进行赋值,这样允许不按照函数定义的参数顺序进行赋值,因为Python解释器这是可以将参数与传入变量进行对应。

1
2
3
4
def test(a, b):
print(a)
print(b)
test(b=2, a=1)

默认参数指的是当参数的值没有在调用时指定的话,函数将为参数赋值一个预先定义好的值,<font color='red'>默认参数的位置应当位于参数列表的最后</font>:

1
2
3
4
5
def test(a, b=5):
print(a)
print(b)
test(1)
test(1, 2)

当函数需要处理的变量的数目不确定的时候可以使用变长参数,变长参数在函数定义的时候名字没有预先确定,在定义函数的时候在变量前面增加一个星号(*)表示这是一个变长变量:

1
2
3
4
5
6
# *args接收单个参数
# **kwargs接收字典型键值对
def printinfo(*args,**kwargs):
print(args)
print(kwargs)
printinfo(1,'asdas',name='asd',age=12)

2.全局变量与局部变量

在函数体中定义的变量具有局部作用域,他们只能在定义他们的函数之内被访问;全局变量可以在程序内被所有的函数访问。

1
2
3
4
5
6
7
# 如果在局部使用全局变量,不加global视为在局部新建一个变量,与原变量地址空间不同。
a = 50
def test():
a = 100
print(a)
test()
print(a)

1
2
3
4
5
6
7
8
# 添加global,标记为全局变量
a = 50
def test():
global a
a = 100
print(a)
test()
print(a)

1
2
3
4
5
6
a = 50
def test():
num = a
print(num)
test()
print(a)

3.参数传递

python中参数的传递都是引用的传递,传递的是地址空间,也可以说传递的是变量所占的地址空间

1
2
3
4
5
6
7
8
9
10
11
12
13
def change_list(mylist,str_):
mylist.append('test')
print('Inside function {}'.format(mylist))
# a指向传过来的str_的地址空间
str_ = str_ + 'def'
print(str_)
lst = [1, 2, 3]
b = 'abc'
change_list(lst,b)
# 经过函数的处理,lst最后增加了一个元素
# 经过函数的处理,a,b都指向'abc'的地址空间
print(lst)
print(b)

4.匿名函数

匿名函数的定义方法与前面讲的传统的使用def的方法是不同,使用lambda关键词定义匿名函数。

  • 匿名函数可以接收任意数目的参数,但是返回值只能是一个值或者表达式,他们不能包含命令或者多个表达式。
  • lambda 函数具有自己的命名空间,他们除了参数列表中的变量以及全局变量之外无法访问其他的变量。
  • lambda 函数无法直接被用来打印变量,lambda 函数需要一个表达式
    1
    2
    3
    sm = lambda x, y: x + y
    res = sm(1, 2)
    print(res)

文件操作

1.open函数

open函数被用来打开或者创建文件,它将会返回一个file对象。

1
file object = open(file_name [, access_mode][, buffering][,encoding])

有几种常见的模式:

  • r 表示以只读方式打开 (默认模式)
  • rb 表示以二进制只读打开
  • r+ 以读写方式打开
  • rb+ 以二进制读写方式打开
  • w 以只写方式打开(如果已有文件,将会覆盖,如果没有将会创建)
  • wb 以二进制只写打开(如果已有文件,将会覆盖,如果没有将会创建)
  • w+ 以读和写的方式打开一个文件(如果已有文件,将会覆盖,如果没有将会创建)
  • wb+ 以二进制方式读和写的方式打开一个文件(如果已有文件,将会覆盖,如果没有将会创建)
  • a 表示 append,追加模式,如果文件存在的话,指针将位于文件的末尾,如果没有文件的话将会创建一个
  • ab 以二进制格式追加模式打开一个文件
  • a+ 以追加和读的方式打开一个文件
  • ab+ 以追加和读的方式打开二进制格式文件

buffer是读写的缓冲区间,有可以选三种值: 0 表示不适用buffer,1 表示 buffer 文件中的一行,其余正整数表示buffer的字节数

encoding是指明对文件编码,仅适用于文本文件。如果不明编码方式,默认是使用locale.getpreferredencoding()函数返回的编码方式。

2.file对象属性

  • file.close 返回文件是否关闭
  • file.mode 返回打开文件的模式
  • file.name 返回文件的名字
    1
    2
    3
    4
    5
    fo = open("foo.txt", "wb")
    print ("Name of the file: ", fo.name)
    print ("Closed or not : ", fo.closed)
    print ("Opening mode : ", fo.mode)
    fo.close()

close()方法 file对象的close()方法用于将没有写入文件的信息写入文件以后关闭文件,<font color='red'>在打开文件以后要记得关闭文件。</font>

3.read()和write()

file 对象可以使用 read() 对文件的内容进行读取,在使用read()对文件进行读取的时候会一次性的读取文件的全部内容。还可以使用readline(),每次读取文件的一行,readlines()则可以一次读取文件中的全部行,并且返回一个列表。

write() 文件可以用于将内容写入文件,在写完文件之后一定要记得使用close()方法保证对于文件的更改已经写入了硬盘。

在对文件进行读写操作的时候,常用的一种方式是使用with,这样Python在对文件读写完成后会自动调用close()方法:

1
2
3
4
5
6
7
8
9
10
11
12
fo = open("foo.txt", "w+")
fo.write("hello world")
fo.close()
fo = open("foo.txt", "r")
t = fo.read()
print(t)

with open("foo.txt", "w+") as f:
for i in range(10):
f.write(str(i) + '\n')
with open("foo.txt", "r") as f:
print(f.readlines())

Python 进阶

1.生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。

在Python中,这种一边循环一边计算的机制,称为生成器generator。

1
2
3
4
5
6
7
8
9
10
11
# 这是一个列表生成式
L = [ x*2 for x in range(5)]
# 最简单的生成器写法
G = ( x*2 for x in range(5))
print(type(L))
print(type(G))
print(next(G))
print(next(G))
print(next(G))
# send输入高级用法
print(G.send(None))

<font color='red'>经常我们的生成的生成器不会使用next(),而是直接使用for循环遍历,而生成器的写法也就是将函数的return变成yield,我们后面的AI学习中可能遇到很大的数据量,因此生成器是我们必不可少的工具</font>

1
2
3
4
5
6
7
8
# 通过生成器实现斐波那契数列
def fib(times):
n = 0
a,b = 0,1
while n<times:
yield b
a,b = b,a+b
n+=1

2. 迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。 1、可迭代对象 可以直接作用于 for 循环的数据类型有以下几种:

  • 一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
  • 一类是 generator ,包括生成器和带 yield 的generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable <font color='red'>迭代器都是可迭代对象</font> 2、迭代器 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。 <font color='red'>生成器都是迭代器</font> 3、iter()函数 生成器都是 Iterator 对象,但 list 、 dict 、 str 虽然是 Iterable ,却不是 Iterator 。 把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用 iter() 函数:

1
2
3
4
# list 是可迭代对象
a = [1,2,3,4]
for i in a:
print(i)

3.装饰器

装饰器是由python闭包系统实现的,本质上,就是一个返回函数的高阶函数,请看:

1
2
3
4
5
6
7
8
9
10
11
12
from time import ctime
def timefun(func):
def wrappedfunc(*args,**kwargs):
print("%s called at %s"%(func.__name__, ctime()))
return func(*args,**kwargs)
return wrappedfunc

@timefun
def test(a,b):
print(a+b)

test(1,2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 通过装饰器验证登陆 只有输入1才可以运行
def login(func):
def wfunc(*args,**kwargs):
pop = input('输入编号')
if pop == '1':
print('欢迎登陆')
return func(*args,**kwargs)
else:
print('陌生访问')
return wfunc

@login
def test(a,b):
print(a+b)

test(1,2)

错误处理

1.异常处理

在python中对异常进行处理时经常使用以下结构:

1
2
3
4
5
6
7
8
try:
pass
except error0:
pass
except error1:
pass
finally:
pass

try后面跟着需要执行的代码块,except后面标注异常的类型,在出现对应的异常的时候执行对应代码块,无论程序是否正确执行finally后面接着的代码块都会在最后执行。Python 中内置的异常类型,参考这个连接

2.Assert语句

assert使用方法:

1
assert Expression[, Expression]

assert 将会判断后面跟的表达式是否为真,如果为false的话将会抛出AssertionError异常,经常可以用于对变量类型,或者程序逻辑进行检查,比如我们写一个计算两个向量内积的程序:

1
2
3
4
5
6
def mul(x, y):
assert len(x) == len(y), "size of 2 vectors should be same"
t = 0
for i in range(len(x)):
t += x[i] * y[i]
return t

Python 爬虫基础、技巧

1.JSON

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,是前后端交互中常用的数据格式,它与python内建的字典很相似,可以相互转换,然后供我们提取数据 python3 中可以使用 json 模块来对 JSON 数据进行编解码,它包含了两个函数:

1
2
json.dumps(): 对数据进行编码,也就是字典变为json.
json.loads(): 对数据进行解码,也就是json变为字典.

<font color='red'>json.dumps()中 可以将ensure_ascii 设为 False可以避免一些不必要的问题</font>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import json
# 一个标准的python字典
data = {
'no' : 1,
'name' : 'Runoob',
'url' : 'http://www.runoob.com'
}
# 将字典转换成json
json_str = json.dumps(data,ensure_ascii=False)

print(json_str)
print(type(json_str))

# 一串json数据
json_str = '{"name": "Runoob", "no": 1, "url": "http://www.runoob.com"}'
# 将json转为字典
dict_ = json.loads(json_str)
print(dict_)
print(dict_['name'])
print(type(dict_))

2.Requests库

Requests is an elegant and simple HTTP library for Python, built for human beings. 简单的请求模式:

1
2
3
4
# get请求
response = requests.get(url, params={}, headers={}, cookie={}, proxies={}, verify=False)
# post请求
response = requests.post(url, data={}, headers={}, cookie={}, proxies={}, verify=False)

response的方法:

1
2
3
4
5
6
response.status_code #响应状态码
response.content #字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩
response.text #字符串方式的响应体,会自动根据响应头部的字符编码进行解码
response.json() #Requests中内置的JSON解码器
response.headers #响应的响应行
response.request.headers #请求的请求行

我们可以通过Cookie或者Session进行模拟登陆,获取登录后的数据。

1
2
3
4
5
6
7
8
9
# Cookie进行模拟登陆
import re
import requests

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36','Cookie':'anonymid=jd5aevsb-2nuy7n; _r01_=1; __utmz=151146938.1520997522.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utma=151146938.942267969.1520997521.1520997521.1520997521.1; depovince=BJ; _de=84C13AEF64FC380CAE794CB9CCA086D2; ln_uact=18610864225; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; jebe_key=59aeee30-3816-4484-9881-4f6690bdce3f%7C100ad246dd59063859de7b1ea3f6ad38%7C1526957380310%7C1%7C1526957382140; jebecookies=74f2005f-ba3d-4a61-a1d0-19948a4d8970|||||; JSESSIONID=abcD_hI_-8WDRf9JK4fow; ick_login=cc4c902c-a43d-4481-8cb3-04ecdc2b9c2f; p=9a54e6619286a17266723fc6b08cc5f04; first_login_flag=1; t=97663cf647a6e85e1bd5d13636b0a2724; societyguester=97663cf647a6e85e1bd5d13636b0a2724; id=963865664; xnsid=7a1d85fa; loginfrom=syshome; wp_fold=0'}
url = 'http://www.renren.com/963865664/profile'
resp = requests.get(url, headers=headers)
html = resp.content.decode()
print(re.findall(r"李**",html))

1
2
3
4
5
6
7
8
9
10
11
12
13
# Session进行模拟登陆
import requests
import re
url = "http://www.renren.com/PLogin.do"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'}
data = {
'email':'186****4225',
'password':'q****6'
}
session_my = requests.session()
session_my.post(url, data=data, headers=headers)
html = session_my.get('http://www.renren.com/963865664/profile', headers=headers).content.decode()
print(re.findall(r"李**",html))

3.Xpath

XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。 lxml库:

  • 导入lxml的etree库
  • 利用etree.HTML,将字符串转化为Element对象
  • Element对象具有xpath的方法
  • lxml 可以自动修正 html 代码

看一个实例

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
ht = """
<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>·

<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>

<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>

<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>

<book category="WEB">
<title lang="en111">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>

</bookstore>
"""

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
from lxml import etree
xml = etree.HTML(ht)
# 由于etree将xml修正,根节点已经不是bookstore,因此我们采用//
# 注意xpath返回的内容都是列表
# 选取所有 title的文本
titles = xml.xpath('//bookstore/book/title/text()')
print(titles)
# 选取第一个 title的文本
one_title = xml.xpath('//bookstore/book[1]/title/text()')
print(one_title)

# 选取价格大于35的book的year信息
year = xml.xpath('//bookstore/book[price>35]/year/text()')
print(year)

# 选取book category="CHILDREN"的书的price
price = xml.xpath('//bookstore/book[@category="CHILDREN"]/price/text()')
print(price)

# 选取book category=“WEB”的书的title的lang属性
lang = xml.xpath('//bookstore/book[@category="WEB"]/title/@lang')
print(lang)

# xpath如果不去选择属性和txt,会返回Element对象,可以对Element对象再次xpath
aaa = xml.xpath('//bookstore/book[@category="WEB"]/title')
print(aaa)
for i in aaa:
bbb = i.xpath('./@lang')
print(bbb)

参考资料

4.反反爬

反反爬虫措施,就是反反爬,当然还有反反反爬虫。 反反爬虫措施有以下几种:

  • 降低访问频率(在requests前加time.sleep())
  • 模拟请求头
  • 模拟登陆
  • 使用代理IP
  • 携带cookie
  • 设置Referer header(躲过referer检测)
  • 逆向js(解密别人的反爬手段,这个比较高级)