Python是一只可爱的爬虫(二)

状态码

1
2
3
4
5
6
7
8
9
import requests
link = "https://ithou.cc"
r = requests.get(link)

# 响应状态码
print(r.status_code)

# 文本编码
print(r.encoding)

r.encoding ==文本编码 #12e912==
r.status_code 服务器响应状态码

  • 200 成功 //但愿如此
  • 3xx 重定向
  • 4xx 客户端错误
  • 5xx 服务器错误

    了解TCP/IP三次握手

    建立起一个TCP连接需要经过 三次握手

    • 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

    • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

    • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主 动关闭连接之前,TCP 连接都将被一直保持下去。

静态网页抓取

定制HTTP请求头

Chrome浏览器访问 https://ithou.cc

F12 或者 鼠标右键检查,然后依次 NetworkAllF5(刷新)

复制上图的 user-agent 里的内容。

1
2
3
headers = {
`user-agent`: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36`
}
1
2
3
4
5
6
7
8
9
10
# 定制HTTP请求头
import requests
key_dict = {'key1': 'value1',
'key2': 'value2'}
link = 'http://httpbin.org'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
}
r = requests.get(link, params=key_dict, headers = headers)
print(r.status_code)

传递URL参数

http://httpbin.org 这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法,对 web 开发和测试很有帮助。它用 Python + Flask 编写,是一个开源项目。

1
2
3
4
5
6
7
8
# 传递URL参数
import requests
key_dict = {'key1': 'value1',
'key2': 'value2'}
link = 'http://httpbin.org/get'
r = requests.get(link, params=key_dict)
print(r.status_code)
print(r.text)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
200
{
"args":{
"key1":"value1","key2":"value2"
},
"headers":
{
"Accept":"*/*",
"Accept-Encoding":"gzip, deflate",
"Connection":"close",
"Host":"httpbin.org",
"User-Agent":"python-requests/2.18.4"
},
"origin":"125.71.5.252",
"url":"http://httpbin.org/get?key1=value1&key2=value2"
}

发送POST请求

遗憾的是,有些请求并不是GET方法,因为url传递参数时,极不安全,比如账号和密码以GET方式发送极不安全,比如,https://ithou.cc/getuser=ithou&password=654321
在这个链接里面以GET方式发送就暴露了用户名和密码。此时应该采用表单式POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
# 发送POST请求

import requests
link = 'http://httpbin.org/post'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
}
key_dict = {'a': 'a',
'b': 'b'}

r = requests.post(link, headers = headers, data = key_dict)
print(r.status_code)
print(r.text)

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
200
{"args":{},
"data":"",
"files":{},
"form":{"a":"a","b":"b"},
"headers":{
"Accept":"*/*",
"Accept-Encoding":"gzip, deflate",
"Connection":"close",
"Content-Length":"7",
"Content-Type":"application/x-www-form-urlencoded",
"Host":"httpbin.org",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36(KHTML, like Gecko) Chrome/67.0.3396.62
Safari/537.36"
},
"json":null,
"origin":"125.71.5.36",
"url":"http://httpbin.org/post"
}

设置超时参数

如果不设置,可爱的爬虫可能会失去响应……
为了测试timeout参数的效果,故设置 timeout=0.001,可以看到:在0.001秒内,网站 https://ithou.cc没有给出任何响应,抛了异常

源码

1
2
3
4
import requests
link = 'https://ithou.cc'
r = requests.get(link, timeout = 0.001)
print(r.status_code)

结果

1
ConnectTimeout: HTTPSConnectionPool(host='ithou.cc', port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.VerifiedHTTPSConnection object at 0x000001FB7125C828>, 'Connection to ithou.cc timed out. (connect timeout=0.001)'))

豆瓣Robots协议

首先可以看下,豆瓣的 Robots协议
链接:https://www.douban.com/robots.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
User-agent: *
Disallow: /subject_search
Disallow: /amazon_search
Disallow: /search
Disallow: /group/search
Disallow: /event/search
Disallow: /celebrities/search
Disallow: /location/drama/search
Disallow: /forum/
Disallow: /new_subject
Disallow: /service/iframe
Disallow: /j/
Disallow: /link2/
Disallow: /recommend/
Disallow: /trailer/
Disallow: /doubanapp/card
Sitemap: https://www.douban.com/sitemap_index.xml
Sitemap: https://www.douban.com/sitemap_updated_index.xml
# Crawl-delay: 5

User-agent: Wandoujia Spider
Disallow: /

Disallow 表示不允许爬取的目录。
Allow 相反,Allow表示允许爬取的目录。

在谷歌Search Console上测试robots

P.S 由于某些原因,谷歌 暂时无法访问,可以用百度站长工具代替

允许爬取目录

Allow

不允许爬取目录:

Disallow

User-agent: Wandoujia Spider Disallow: /,这句代码的意思是:豆瓣不允许豌豆荚爬取任何信息 ,哈哈哈哈哈哈哈,笑出声~

#Crawl-delay: 5 表示两次下载请求之间应延迟5秒抓取,这样做是为了避免服务器过载

sitemap 表示告诉爬虫,网站地图,以便更好的爬取信息。比如:https://ithou.cc/robots.txt 我博客上的Robots协议

爬取豆瓣电影TOP250

源码

下载: https://github.com/ithou/Cute-Python-Spider

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
40
# ! /usr/bin/python
# Do:爬取豆瓣电影Top250
# Coding: utf-8
# The Script By https://github.com/ithou

import requests #导入request库
from bs4 import BeautifulSoup #从bs4导入BeautifulSoup库,注意大小写,简称BS

#定制HTTP请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
'Host': 'movie.douban.com'
}

def get_movies():
movie_list = []

#利用for循环翻页
j = 1
for i in range(0, 10):
link = 'https://movie.douban.com/top250?start=' + str(i * 25)
r = requests.get(link, headers = headers, timeout = 10) #timeout设置超时时间,一旦超过这个时间,脚本即刻停止
print(str(i + 1), "页响应状态码", r.status_code)

#利用BeautifulSoup解析网页
soup = BeautifulSoup(r.text, "lxml") #生成BS对象
dev_list = soup.find_all('div',class_='hd') #找到<div class="hd">

#遍历刚才找到的<div class="hd">
for each in dev_list:
movie_name = each.a.span.text.strip()
movie_list.append(movie_name) #加入到一个空“列表”,在16行

#存储数据到当前目录的 movie_top250.txt
with open("movie_top250.txt", "a+") as f:
f.write(str(j) + ". " + "《"+ movie_name + "》\n")
f.close() #每次写入数据之后,安全关闭文件
j += 1 #电影序号自增
return movie_list
get_movies()

动态网页抓取(难)

解析真实地址

使用 Chrome浏览器检查 功能虽然可以找到评论区的真实地址,但是此方法仅限于数据量少的情况,一旦评论数众多,此方法就显得黔驴技穷了。

通过Selenium模拟浏览器抓取(难)

1
2
3
4
from selenium import webdriver

driver = webdriver.Chrome() # 绑定chrome浏览器,Firefox等等
driver.get("https://www.python.org/") # chrome自动打开次页面

webdriver 提供了许多寻找网页元素的方法
输入框可以通过 find_element(s)_by_* 方法寻找 name 属性来确定。

单个元素

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

多个元素

find_elements_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector

比如我想找到下面这个元素:

1
<input type="text" name="passwd" id="passwd-id" />

可以有以下几种方法:

1
2
3
4
element = driver.find_element_by_id("passwd-id")
element = driver.find_element_by_name("passwd")
element = driver.find_elements_by_tag_name("input")
element = driver.find_element_by_xpath("//input[@id='passwd-id']")

实现利用selenium的webdriver自动打开 www.python.org 并键入数据自动搜索

源码

1
2
3
4
5
6
7
8
9
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()
driver.get("https://www.python.org/")

elem = driver.find_element_by_name('q')
elem.send_keys('python')
elem.send_keys(Keys.RETURN)

分析、原理

方法作用备注
driver = webdriver.Chrome()绑定Chrome浏览器
driver.get("https://www.python.org/")打开网页
elem = driver.find_element_by_name('q')利用find_element_by_name找到搜索框
elem.send_keys('python')在搜索框键入(Keys)数据
elem.send_keys(Keys.RETURN)搜索相当于Enter

添加User-Agent

User-Agent用于模拟访问,webdriver也可以添加。

模拟Android访问:

1
2
3
4
5
6
7
from selenium import webdriver

## 模拟安卓打开百度首页
options = webdriver.ChromeOptions()
options.add_argument('user-agent="Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"')
driver = webdriver.Chrome(chrome_options=options)
driver.get('https://www.baidu.com')
———— The End ————