Python_Crawling学习记录

为了调用API跑来学爬虫。


# 序列化和反序列化

在文件写入的时候,只能将字符串写入到文件中,像字典、列表那些都不能直接写入,必须先执行序列化才能写入文件。

序列化:把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

使用 json 模块的 dumps()  dump() 函数都可以进行序列化。不同的是,后者能够在参数里填写打开的文件以减少步骤。

# 使用dumps()方法
import json

file_1 = open("test.txt", mode="w")
tiny_list = ["a", "b"]
file_1.write(json.dumps(tiny_list))
file_1.close()


# 使用dump()方法
import json

file_1 = open("test.txt", mode="w")
tiny_list = ["b", "a"]
json.dump(tiny_list, file_1)
file_1.close()

反序列化:

# 使用loads()方法
import json

file_1 = open("test.txt", mode="r")
file_content = json.loads(file_1.read())
file_1.close()
print(type(file_content))


# 使用load()方法
import json

file_1 = open("test.txt", mode="r")
file_content = json.load(file_1)
file_1.close()
print(type(file_content))

# urllib

Python 自带的库,能够模拟浏览器向服务器发送请求。

import urllib.request

# 定义一个URL
url = "https://baidu.com"

# 使用urllib模拟浏览器打开url,并接收返回的response
response = urllib.request.urlopen(url)

# 读取response的内容,read()返回的是字节形式的二进制数据
# 需要将二进制数据转换为字符串,这个过程叫解码 decode
# 使用meta标签里的charset来decode
content = response.read().decode("utf-8")

print(content)

上述代码中,content 的数据类型是 HTTPResponse。下面介绍一些方法:

# 读取前6个字节
response.read(6)

# 读取一行
response.readline()

# 按行读取,直到读完
response.readlines()

# 获取状态码
response.getcode()

# 获取url
response.geturl()

# 获取响应头(状态信息)
response.getheaders()

通过urllib下载文件:

import urllib.request

# 定义一个文件链接
url_download = "xxx.jpg"
# 使用urlretrieve()下载文件
urllib.request.urlretrieve(url_download, "想要的名字.格式")

URL定制:

协议+主机+端口号+路径+参数+锚点

参数是 ? 之后用&连接起来的那一串。锚点是 # 之后的数据。

由于网站的反爬,我们需要打开自己的浏览器通过 Inspect -> Network,选择主机找到自己真实的UA。并将UA放到一个定义的字典里。但 urlopen() 不能传递字典参数,但可以传递request对象,所以要进行URL定制。

url = "https://baidu.com/"
headers = {"User-Agent": "Mozilla/5.0"}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)

编解码:

之前在 url 里直接写中文的方式是错误的,需要使用 urllib.parse.quote() 方法将汉字转换为对应的Unicode。

import urllib.parse

url = "https://baidu.com/s?wd=" + urllib.parse.quote("中文测试")

但如果URL中参数有多个的时候,使用 urllib.parse.quote() 就会比较麻烦。所以下面提出 urllib.parse.urlencode() 方法的使用。这个方法不仅可以将汉字转换为 unicode 编码,还能将字典中的键值对用 & 连接起来。

import urllib.parse

base_url = "www.baidu.com/s?"
para = {
    "wd": "测试",
    "gender": "无性别",
    "cc": "参数"
}
url = base_url + urllib.parse.urlencode(para)

POST请求:

POST 请求参数不会拼接在 URL 后面,需要使用 URL定制。且 POST 请求必须编码成字节型数据,需要使用 urlencode().encode(“utf-8”) 。接下来调用百度翻译作为演示。

import urllib.request
import urllib.parse
import json

headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"}
base_url = "https://fanyi.baidu.com/sug"
# post的参数在Form Data里找
post_data = {"kw": "crawl"}
# 对POST请求进行urlencode()之后还要encode("utf-8")
request = urllib.request.Request(url=base_url, headers=headers, data=urllib.parse.urlencode(post_data).encode("utf-8"))
response = urllib.request.urlopen(request)
content = response.read().decode("utf-8")
# 需要将返回的json数据转换成json对象才能显示结果
print(json.loads(content))

请求百度翻译的详细翻译,涉及到 Cookie 反爬以及多个 POST 请求参数。

import urllib.request
import urllib.parse
import json

headers = {"Cookie": "BIDUPSID="}
base_url = "https://fanyi.baidu.com/v2transapi?from=en&to=zh"
post_data = {
    "from": "en",
    "to": "zh",
    "query": "detail",
    "simple_means_flag": "3",
    "sign": "756271.1009950",
    "token": "099f4a898364b930ffabbde1b1e2af1e",
    "domain": "common"
}
request = urllib.request.Request(url=base_url, headers=headers, data=urllib.parse.urlencode(post_data).encode("utf-8"))
response = urllib.request.urlopen(request)
content = response.read().decode("utf-8")
print(json.loads(content))

AJAX的GET请求:

首先写一个获取豆瓣第一页推荐电影并保存到本地的代码。

import urllib.request
import urllib.parse

base_url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"}
request = urllib.request.Request(url=base_url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode("utf-8")
# 下载数据到本地,因为数据有中文,所以保存的时候应该加一个encoding参数
doban_file = open("douban.json", mode="w", encoding="utf-8")
doban_file.write(content)
doban_file.close()

因为豆瓣排行榜页面用了动态加载技术,所以需要输入指令让计算机翻到下一页,然后爬取下一页的内容。在豆瓣这个案例中,需要清空一下 NETWORK,往下翻找到第二页的接口,对比一下一二页接口的规律,然后写出前十页的接口依次访问获取数据。

import urllib.request
import urllib.parse

base_url = "https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action=&"
start_page = input("请输入起始页数:")
end_page = input("请输入终止页数")
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36"}


def init_request(num: int):
    para = {
        "start": (num - 1) * 20,
        "limit": 20
    }
    url = base_url + urllib.parse.urlencode(para)
    inited_request = urllib.request.Request(url=url, headers=headers)
    return inited_request


def process_crawling(a_request):
    a_response = urllib.request.urlopen(a_request)
    return a_response


def download_json(a_response, num):
    content = a_response.read().decode("utf-8")
    a_json = open("douban_" + str(num) + ".json", "w", encoding="utf-8")
    a_json.write(content)
    a_json.close()


for page_num in range(int(start_page), int(end_page) + 1):
    request = init_request(page_num)
    response = process_crawling(request)
    download_json(response, page_num)
print("工作完成")

重点是从浏览器 NETWORK 里面找数据包里的地址,而不是看浏览器地址。

AJAX的POST请求:

当在头部中看到 X-Requested-WidthXMLHttpRequest 时应当判断为AJAX响应。同时还要注意 Form Data 里的键值对。

其他操作和上述POST请求一致。

异常:

在执行过程中可能会遇到URLError/HTTPError,后者是前者的子类。

Cookie登陆:

有些数据需要登陆平台之后才能采集,这里就需要使用Cookie登陆。登陆后在 Request Headers 中找到 Cookie & Referer ,前者是登陆信息,后者突破防盗链需要用到。在爬取数据过程中一般来说只需要这两个要素,但有些网站需要用到动态Cookie,在下面 Handler 小节中讲解。

Handler处理器:

Handler处理器可以定制更高级的请求头,包括动态Cookie和代理等。使用Handler处理器有三步,需要按步骤执行。

import urllib.request

base_url = "https://www.baidu.com/"
headers = {
    'User-Agent': 'Mozilla/5.0'
}
request = urllib.request.Request(url=base_url, headers=headers)
# 创建Handler对象
handler = urllib.request.HTTPHandler()
# 创建opener对象
opener = urllib.request.build_opener(handler)
# 调用open方法
response = opener.open(request)
content = response.read().decode("utf-8")
print(content)

设置代理:

import urllib.request

base_url = "https://www.baidu.com/"
headers = {
    'User-Agent': 'Mozilla/5.0'
}
request = urllib.request.Request(url=base_url, headers=headers)
# 创建proxy字典
proxy = {
    "http": "127.0.0.1:2233"
}
# 创建ProxyHandler对象
proxy_handler = urllib.request.ProxyHandler(proxies=proxy)
# 创建opener对象
opener = urllib.request.build_opener(proxy_handler)
# 调用open方法
response = opener.open(request)

代理可以使用快代理或其他服务商获得。但如果高强度获取某个服务,则需要使用代理池。所谓代理池是一个列表,放着代理字典。

proxy_pool = [
    {"http": "127.0.0.1:2233"}
    {"http": "127.0.0.2:2234"}
]

import random
proxy = random.choice(proxy_pool)

看69

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments