目录
一、 初识爬虫
1. 简单的爬虫
from urllib.request import urlopen
url = "http://www.baidu.com/"
resp = urlopen(url)
message = resp.read().decode("utf-8")
print(message)
with open("my_spyder.html", mode="w", encoding="utf-8") as f:
f.write(message)
print("over!")
open的时候要指定encoding是utf-8,windows系统默认gbk,会乱码。
baidu网址输入的时候注意是http,没有s。
2. web请求
1.服务器渲染:再服务器那边直接把数据和html整合在一起,统一返回给浏览器
再页面源代码中能看到数据
2.客户端渲染:第一次请求html骨架,第二次请求数据。进行数据展示
在页面源代码中看不到数据
熟练使用浏览器抓包工具 f12里看
3. http协议
请求:
- 请求行:包括请求方式,请求url地址,协议
- 请求头:放一些服务器要使用的附加信息
- 请求体:一般放一些请求参数
响应:
- 状态行:协议 状态码
- 响应头:放一些客户端要使用的一些附加信息
- 响应体:服务器返回的真正客户端要用的内容
请求头重最常见的一些重要内容:
- User-Agent:请求载体的身份识别(用什么发送的请求)
- Referer:防盗链(这次请求从哪个页面来?反爬用到)
- cookie:本地字符串数据信息(用户登录信息,反爬的token)
响应头重一些重要的内容:
- cookie:本地字符串数据信息
- 各种神奇的莫名其妙的字符串(需要经验,一般都是token字样,防止各种攻击和反爬)
请求方式:
- GET: 显式提交
- POST: 隐式提交
4. requests入门
import requests
url = "https://www.sogou.com/web?query=周杰伦"
resp = requests.get(url)
print(resp)
此处用vscode一直报错无法找到requests,换用pycharm可以使用,输出为200.
不知道为什么。
print(resp.text)
输出页面后被拦截,因为被检测到是自动程序发出的。
打开指定页面,审查元素找到User-Agent复制过来。用来伪装成正常访问请求。
url = "https://www.sogou.com/web?query=周杰伦"
dic = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
}
resp = requests.get(url, headers=dic)
修改请求头。
审查元素可以看到用的是post还是get。
百度翻译使用的是get。使用英文输入法输入单词,在network中监视,找到sug,发现翻译是通过https://fanyi.baidu.com/sug实现,并且使用的是GET。
找到form-data为输入的表单信息,因此只要替换form-data就可以实现自定义输入。
import requests
url = "https://fanyi.baidu.com/sug"
s = input("输入需要翻译的英文单词:")
dat = {
"kw": s
}
# 发送post请求,发送数据必须放在字典中
resp = requests.post(url, data=dat)
print(resp.json()) # 将服务器返回的内容直接返回为json() =>字典
直接resp.text会出现乱码,用json可以解释成字典。
请求url太长可以考虑封装
这些内容都包含在url里。
url = "https://movie.douban.com/typerank?type_name=%E5%96%9C%E5%89%A7&type=24&interval_id=100:90&action="
重新封装
url = "https://movie.douban.com/j/chart/top-list"
# 重新封装参数
param = {
"type": "24",
"interval_id": "100:90",
"action": "",
"start": 0,
"limit": 20
}
requests.get(url=url, params=param)
print(resp.text)
如果发现异常,说明可能被挡了,优先考虑user-agent。添加后就可以了
import requests
url = "https://movie.douban.com/j/chart/top_list"
# 重新封装参数
param = {
"type": "24",
"interval_id": "100:90",
"action": "",
"start": 0,
"limit": 20
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
}
resp = requests.get(url=url, params=param, headers=headers)
print(resp.json())
二、数据解析与提取
1. 概述
本课程提供三种解析方式:
- re解析,运行最快
- bs4解析,最简单,但效率不高
- xpath解析,比较流行,写起来简单
2. Regular Expression 正则表达式
上手难度高。速度快,效率高,准确性高。
正则的语法:使用元字符进行排列组合用来匹配字符串
语法测试网站:开源中国-正则表达式测试
每个元字符默认只匹配一个字符串。
. 匹配除换行符以外的任意字符
\w 匹配字母数字下划线
\s 匹配任意空白符
\d 匹配数字
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开始(开始的时候必须符合要求
$ 匹配字符串的结尾(结尾的时候除了要求,不能有别的
\W 匹配非字母数字下划线
\D 匹配非数字
\S 匹配非空白符
a|b 匹配字符a或字符b
() 匹配括号内的表达式,也表示一个组
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中字符的所有字符
量词:
控制前面的元字符出现的次数
* 重复零次或多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
.* 贪婪匹配(尽可能多的匹配)
.*? 惰性匹配(尽可能少的,可以理解为就近匹配)
3. re模块
# findall: 匹配字符串中所有的符合正则的内容,返回的是列表 不常用
lst = re.findall(r"\d+", "我的电话号码是:123, 我朋友的电话:456")
print(lst)
# finditer: 匹配字符串中所有的内容,返回的是迭代器
it = re.finditer(r"\d+", "我的电话号码是:123, 我朋友的电话:456")
print(it)
for i in it:
print(i.group())
# search,全文匹配,找到一个结果就返回 返回的是match对象,拿数据需要.group()
s = re.search(r"\d+", "我的电话号码是:123, 我朋友的电话:456")
print(s.group())
# match是从头开始匹配
s = re.match(r"\d+", "123, 我朋友的电话:456")
print(s)
# 预加载正则表达式 返回迭代器
obj = re.compile(r"\d+")
ret = obj.finditer("123, 我朋友的电话:456")
for it in ret:
print(it.group())
ret = obj.findall("今天100000度")
print(ret)
# (?P<分组名>正则) 可以单独从正则匹配的内容中进一步提取内容
obj = re.compile(r"<div class='.*?'><span id='(?P<id>\d)+'>(?P<number>.*?)</span></div>", re.S)
# re.S 让.能匹配换行符
result = obj.finditer(s)
for it in result:
print(it.group("number"))
print(it.group("id"))
()相当于隔开,c语言里的大括号。
?P<name>用这个为正则表达式命名,之后直接用名字调用
4. 豆瓣电影练习
import requests
import re
import csv
url ="https://movie.douban.com/top250"
dic = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Mobile Safari/537.36 Edg/98.0.1108.56"
}
resp = requests.get(url, headers=dic)
page_content = resp.text
# 解析数据
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)'
r'</span>.*?<p class="">.*?<br>(?P<year>.*?) '
r'.*?<span class="rating_num" property="v:average">(?P<score>.*?)</span>'
r'.*?<span>(?P<aud_num>.*?)人评价</span>', re.S)
# 开始匹配
result = obj.finditer(page_content)
f = open("data.csv", mode="w", encoding="utf-8")
csvwriter = csv.writer(f)
for it in result:
# print(it.group("name"))
# print(it.group("year").strip())
# print(it.group("score"))
# print(it.group("aud_num"))
dic = it.groupdict()
dic['year'] = dic['year'].strip()
csvwriter.writerow(dic.values())
resp.close()
5. 电影天堂练习
1. 定位到2022必看片
2. 从2022必看片中提取子页面的链接地址
3. 请求子页面的链接地址,拿到所需的下载地址
import re
import requests
domain = "https://dytt89.com/"
resp = requests.get(domain, verify=False)
resp.encoding = 'gb2312' # 审查元素找到mata后,指定字符集
# print(resp.text)
# 那到ul里面的li
obj1 = re.compile(r"2022必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)
obj2 = re.compile(r"<a href='(?P<href>.*?)'")
obj3 = re.compile(r'◎片 名 (?P<movie>.*?)<br />.*? '
r'<td style="WORD-WRAP: break-word" '
r'bgcolor="#fdfddf"><a href="(?P<download>.*?)">', re.S)
result1 = obj1.finditer(resp.text)
child_href_list = []
for it in result1:
ul = it.group('ul')
# 提取子页面连接
result2 = obj2.finditer(ul)
for itt in result2:
# 拼接子页面的url地址: 域名+子页面地址
child_href = domain + itt.group('href').strip('/')
child_href_list.append((child_href))
# 提取子页面内容
for href in child_href_list:
child_resp = requests.get(href, verify=False)
child_resp.encoding = 'gb2312'
result3 = obj3.search(child_resp.text)
print(result3.group("movie"))
print(result3.group("download"))
resp.close()
6. bs4 北京新发地、热搜榜
1. 拿到页面源代码
2. 使用bs4进行解析,拿到数据
import requests
import re
from bs4 import BeautifulSoup
import csv
# 原网址有变化,用之前的方法可以提取
#
# url = "http://www.xinfadi.com.cn/getPriceData.html"
# resp = requests.get(url)
# obj = re.compile(r'.*?"prodName":"(?P<name>.*?)","prodCatid".*?')
#
# result = obj.finditer(resp.text)
# for it in result:
# dic = it.groupdict()
#
# print(dic['name'])
# 视频教程方法
# 使用微博热搜榜
url = "https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6"
dic = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 Edg/98.0.1108.56",
"cookie": "f12 复制"
}
resp = requests.get(url, headers=dic)
f = open("resou.csv", mode="w", encoding="utf-8")
csvwriter = csv.writer(f)
# print(resp.text)
# 解析数据
# 1. 把页面交给BeautifulSoup进行处理,生成bs对象
page = BeautifulSoup(resp.text, "html.parser") # 指定html解析器
# 2. 从bs对象中查找数据
# find(标签, 属性=值)
# find_all(标签, 属性=值)
tbody = page.find("tbody") # 括号里可以加上 attr={"class":""}指定
# 拿到所有数据
trs = tbody.find_all("a")[1:] # 一层层套
for tr in trs: # tr为每一行数据
name = tr.text
csvwriter.writerow([name])
f.close()
resp.close()
7. 抓取图库图片
1. 拿到主页源代码,然后提取子页面连接,href
2. 通过href拿到子页面内容,找到 img->src
3. 下载图片
import requests
from bs4 import BeautifulSoup
import time
url = "https://www.umeitu.com/bizhitupian/weimeibizhi/"
resp = req