一、Python 基础
1.1 pip
指令 | 含义 |
---|---|
pip -V |
查看 pip 版本 |
pip install pagckageName |
安装指定的包 |
pip uninstall packageName |
删除指定的包 |
pip list |
显示已安装的包 |
pip freeze |
显示已安装的包,并且以指定的格式显示 |
pip install packageName -i url |
修改 pip 下载源 |
下载源:
1.2 数据类型
Number:数字(int、
long、float、bool、complex)# int money = 5000 # float money1 = 55.55 # boolean gender = True sex = False # complex complexNum1 = 1 + 2j complexNum2 = 1 + 2j print(complexNum1 + complexNum2)
String:字符串
# 字符串 hello = 'hello' world = "world" helloWorld = '"HelloWorld"' hello_world = "'Hello World'"
List:列表
# 列表 classList = ['软件1901', '软件1902'] # <class 'list'> print(type(classList))
Tuple:元组。元组与列表的区别在于元组不可修改,定义只有一个元素的元组时需要在元素后添加一个逗号以标识该变量表示的是元组
cities = ('武汉') # output: <class 'str'> print(type(cities)) # output: <class 'tuple'> cities = ('武汉',) print(type(cities))
Set:集合
# 集合 classSet = {'软件1901', '软件1902'} # <class 'set'> print(type(classSet))
Dictionary:字典
# 字典 personDict = {'name': 'Spring-_-Bear', 'age': 22} # <class 'dict'> print(type(personDict))
数据类型转换:
函数 说明 int(x) 将 x 转换为一个整数 float(x) 将 x 转换为一个浮点数 str(x) 将 x 转换为一个字符串 bool(x) 将 x 转换为一个布尔值 # output: 55 print(int(55.55)) # output: 1 print(int(True)) # output: 0 print(int(False)) # output: True print(str(True)) # output: False print(str(False)) # output: False 0 为假,非 0 为真 print(bool(0)) print(bool(0.0)) print(bool(0 + 0j)) print(bool('')) print(bool([])) print(bool(())) print(bool({}))
1.3 运算符
与 | 或 | 非 |
---|---|---|
and | or | not |
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | 10 + 20 = 30 |
- | 减 | 10 - 20 = -10 |
* | 乘 | 10 * 20 = 200 |
/ | 除 | 50 / 2 = 25.0,100 / 3 = 33.333333333333336 |
// | 取整除 | 50 // 2 = 25,100 // 3 = 33 |
% | 取余 | 100 % 3 = 1 |
** | 指数 | 2 ** 10 = 1024 |
() | 括号 | 提升运算优先级 |
复合赋值:
a, b, c = 1, 2, 3 # output: 1 2 3 print(a, b, c)
字符串拼接:
# output: TypeError: can only concatenate str (not "int") to str print('123' + 456) # output: 你爱我 我爱你 蜜雪冰城甜蜜蜜你爱我 我爱你 蜜雪冰城甜蜜蜜你爱我 我爱你 蜜雪冰城甜蜜蜜 print('你爱我 我爱你 蜜雪冰城甜蜜蜜' * 3)
1.4 关键字
# 33 个 Python 关键字
False None True and as assert break class
continue def del elif else except finally for
from global if import in is lambda nonlocal
not or pass raise return try while with
yield
1.5 流程控制
if-elif-else:
score = int(input('Please input your score:')) if score >= 90: print('优秀') elif score >= 80: print('良好') elif score >= 60: print('及格') else: print('不及格')
for:
# range(10) -> [0, 10) for i in range(10): print(i) # range(5, 10) -> [5,10) for i in range(5, 10): print(i) # range(1,10,2) -> [1,3,5,7,9] for i in range(1, 10, 2): print(i) # 通过下标遍历列表 nameList = ['张三', '李四', '王五', '赵六'] for i in range(len(nameList)): print(nameList[i])
1.6 字符串函数
函数 | 功能 |
---|---|
len(s) | 返回字符串长度 |
s1.find(s2) | 返回 s2 在 s1 中首次出现的下标,未出现则返回 -1 |
s1.startswith(s2) | 判断 s1 是否以 s2 开头,是则返回 True,否则返回 False |
s1.endswith(s2) | 判断 s1 是否以 s2 结尾,是则返回 True,否则返回 False |
s1.count(s2) | 统计 s2 在 s1 中出现的次数 |
s1.replace(s2, s3) | 将 s1 中所有的 s2 替换为 s3 |
s1.split(s2) | 以 s2 为分隔符切割 s1,返回列表 |
s1.upper() | 将 s1 转换为全大小 |
s1.lower() | 将 s1 转换为全小写 |
s1.strip() | 去除 s1 的首尾空格 |
s1.join(s2) | 在 s2 中间隔插入 s1,'23'.join('11111') => '1231231231231' |
1.7 列表函数
函数 | 功能 |
---|---|
list1.append(obj) | 将 obj 追加到列表 list1 尾 |
list1.insert(index, obj) | 将 obj 插入到列表 list1 的 index 位置 |
list1.extend(list2) | 将列表 list2 中的元素追加到 list1 中 |
del list[index] | 根据下标 index 移除列表 list1 中的元素 |
list1.pop() | 移除列表 list1 中的最后一个元素 |
list1.remove(key) | 根据 key 移除列表 list1 中的元素 |
# 列表查找
cities = ['武汉', '北京', '上海', '深圳']
city = input('请输入您想查找的城市名称:')
if city in cities:
print('成功找到%s' % city)
else:
print('查找失败')
if city not in cities:
print('%s不在城市列表中' % city)
else:
print('成功找到%s' % city)
1.8 字典函数
字典查询:
person = {'name': '张三', 'age': 22} # output: 22 若 key 不存在则抛出异常 print(person['age']) # output: 张三 若 key 不存在不抛出异常并返回 None print(person.get('name')) # output: 180 若 key 不存在则返回指定的默认值 print(person.get('height', 180))
字典修改:
person = {'name': '张三', 'age': 22} person['name'] = '法外狂徒'
字典添加:
person = {'name': '张三', 'age': 22} person['sex'] = True
字典删除:
person = {'name': '张三', 'age': 22} # 根据 key 删除字典中的某一项 del person['age'] # 删除整个字典 del person # 清空字典中的所有元素 person.clear()
字典遍历:
person = {'name': '张三', 'age': 22} # 遍历所有的 key for key in person.keys(): print(key) # 遍历所有的 val for val in person.values(): print(val) # key-val for key, val in person.items(): print('%s-%s' % (key, val)) # (key, val) for item in person.items(): print(item)
1.9 切片
切片语法格式:[start, end, step]
,从 [start, end)
中以步长 step
为单位截取子部分
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# output: [1, 3, 5, 7, 9]
print(numbers[0: len(numbers): 2])
1.10 函数
def add(a, b):
return a + b
print(add(2, 3))
1.11 IO
在 Python 中使用 open(path, mode)
函数可以打开一个已经存在的文件或者创建一个新文件
模式 | 说明 |
---|---|
r | 以只读模式打开文件并将文件指针置于文件开头,若文件不存在则报错(默认模式) |
w | 以只写模式打开文件,若文件已存在则覆盖,不存在则新建 |
a | 以追加模式打开文件,若文件已存在则将文件指针置于文件末尾,不存在则新建 |
r+ | 打开一个文件用于读写,文件指针置于文件开头 |
w+ | 打开一个文件用于读写,若文件已存在则覆盖,不存在则新建 |
a+ | 打开一个文件用于追加,若文件已存在则将文件指针置于文件末尾,不存在则新建 |
rb | 以二进制格式打开文件用于只读,文件指针置于文件开头 |
wb | 以二进制方式打开文件用于只写,若文件不存在则新建,已存在则覆盖 |
ab | 以二进制方式打开文件用于追加,若文件已存在则将指针置于文件末尾,不存在则新建 |
rb+ | 以二进制格式打开一个文件用于读写,文件指针置于文件开头 |
wb+ | 以二进制格式打开一个文件用于读写,若文件不存在则新建,已存在则覆盖 |
ab+ | 以二进制格式打开文件用于追加读写,若文件已存在则将指针置于文件末尾,不存在则新建 |
# 文件写入
fp = open('file.txt', 'a')
fp.write("Hello python!\n" * 5)
fp.close()
fp = open('file.txt', 'r')
# 文件读取:默认情况下一字节一字节地读,读完整个文件
content = fp.read()
print(content)
# 文件读取:读取一行
line = fp.readline()
print(line)
# 文件读取:读取所有行,返回列表
lines = fp.readlines()
print(lines)
fp.close()
1.12 序列化
dumps
序列化与loads
反序列化:dumps
对象序列化import json person = {'name': 'Spring-_-Bear', 'age': 22} # 序列化,将 Python 对象转换为 json 字符串 person = json.dumps(person) # output: <class 'str'> print(type(person)) fp = open('json.txt', 'w') fp.write(person) fp.close()
loads
反序列化import json fp = open('json.txt', 'r') content = fp.read() # 反序列化 obj = json.loads(content) print(obj) fp.close()
dump 序列化与 load 反序列化:
dump
对象序列化import json person = {'name': 'Spring-_-Bear', 'age': 22} fp = open('json.txt', 'w') # 序列化,将 Python 对象转换为 json 字符串的同时写入文件 json.dump(person, fp) fp.close()
load
反序列化import json fp = open('json.txt', 'r') obj = json.load(fp) print(obj) fp.close()
1.13 异常
try:
num = 1 / 0
print(num)
except ArithmeticError:
print('500 internal exception')
二、urllib
2.1 基本使用
import urllib.request
url = 'http://www.baidu.com'
# 发起请求
response = urllib.request.urlopen(url)
# <class 'http.client.HTTPResponse'>
print(type(response))
# 获取响应的内容,逐字节读取直至读完
content = response.read().decode('utf-8')
# 逐行读取
line = response.readline()
# 读取所有行
lines = response.readlines()
# 响应码
code = response.getcode()
# 请求的 url
url = response.geturl()
# 响应头
headers = response.getheaders()
2.2 文件下载
import urllib.request
url = 'https://whut.springbear2020.cn/static/img/WHUT.png'
filename = 'WHUT.jpg'
urllib.request.urlretrieve(url, filename)
2.3 请求对象定制
import urllib.request
url = 'https://www.baidu.com'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
# 请求对象定制
request = urllib.request.Request(url=url, headers=headers)
# 发起请求
response = urllib.request.urlopen(request)
# 获取响应内容
content = response.read().decode('utf-8')
print(content)
2.4 GET 请求
quote
方法将内容编码为 Unicodeimport urllib.request import urllib.parse data = input('Input the content you want to baidu:') # quote 方法将内容编码为 Unicode url = 'https://www.baidu.com/s?wd=' + urllib.parse.quote(data) headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' } # 请求对象定制 request = urllib.request.Request(url=url, headers=headers) # 发起请求 response = urllib.request.urlopen(request) # 读取响应内容 content = response.read().decode('utf-8') print(content)
urlencode
方法将字典数据拼接为 key&val 形式import urllib.parse data = { 'name': '陆平彪', 'sex': '男' } url = 'https://www.baidu.com/s?wd=' + urllib.parse.urlencode(data) print(url)
2.5 POST 请求
import urllib.parse
import urllib.request
import json
url = 'https://fanyi.baidu.com/sug'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
data = {
'kw': 'spider'
}
# 对请求参数内容进行编码,并转为字节数据
bytesData = urllib.parse.urlencode(data).encode('utf-8')
# 请求对象定制
request = urllib.request.Request(url, bytesData, headers)
# 发起请求,获取响应
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
with open('fanyi.json', 'w', encoding='utf-8') as fp:
fp.write(content)
fp.close()
2.6 代理
import urllib.request
url = 'https://movie.douban.com/j/chart/top_list?type=17&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/105.0.0.0 Safari/537.36'
}
proxies = {
'http': '188.131.233.175:8118'
}
# 创建代理处理器 handler
handler = urllib.request.ProxyHandler(proxies=proxies)
# 依据 handler 生成 opener
opener = urllib.request.build_opener(handler)
# 请求对象定制
request = urllib.request.Request(url=url, headers=headers)
# 通过 opener 发起请求
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)
三、内容解析
3.1 xpath
安装 lxml:
pip install lxml -i https://pypi.douban.com/simple
lxml 的两种使用方式:
from lxml import etree # 解析本地 html 页面 localHtml = etree.parse('xpath.html') # 解析响应的 html 页面 etree.HTML(response.read().decode('utf-8'))
xpath 基本语法:
方式 示例 说明 路径查询 // 查找所有子孙节点,不考虑层级关系 路径查询 / 查找父节点下的子节点 谓词查询 //div[@id] 查找拥有 id 属性的 div 谓词查询 //div[@id=“main”] 查询 id=main 的 div 属性查询 //div[@id=“main”/@class] 查询 id=main 的 div 的 class 属性值 模糊查询 //div[contains(@id, “he”)] 查询 id 值包含 he 的 div 模糊查询 //div[starts-with(@id, “he”)] 查询 id 值以 he 开头的 div 内容查询 //div/text() 查询 div 的文本值 逻辑运算 //div[@id=“head” and @class=“c1”] 查询 id=head 且 class=c1 的 div 逻辑运算 //div[@class=“c1”] |//div[@class=“c2”] 获取 class=c1 或 class=c2 的 div xpath 使用示例:
from urllib import request from lxml import etree def send_request(url, headers): # 请求对象定制 request_obj = request.Request(url=url, headers=headers) # 发起请求,获取响应 response = request.urlopen(request_obj) # 读取响应内容 return response.read().decode('utf-8') def parse_content(content): html = etree.HTML(content) # xpath 提取需要的内容 url_list = html.xpath('/html/body/div[3]/div[2]//img/@data-original') name_list = html.xpath('/html/body/div[3]/div[2]//img/@alt') # 下载图片 for i in range(len(url_list)): request.urlretrieve(url='https:' + url_list[i], filename='./img/' + name_list[i] + '.jpg') if __name__ == '__main__': headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' } # 循环页码发起请求 for i in range(1, 2): url = '' if i == 1: url = 'https://sc.chinaz.com/tupian/ribenmeinv.html' else: url = 'https://sc.chinaz.com/tupian/ribenmeinv' + '_' + str(i) + '.html' response = send_request(url, headers) parse_content(response)
3.2 jsonpath
安装 jsonpath:
pip install jsonpath -i https://pypi.douban.com/simple
jsonpath 与 xpath 的语法对比:
- [] 在 xpath 表达式总是从前面的路径来操作数组,索引是从 1 开始
- 使用 JOSNPath 的 [] 操作符操作一个对象或者数组,索引是从 0 开始
xpath jsonpath 结果 /store/book/author $.store.book[*].author 书店里所有图书的作者 //author $…author 所有的作者 /store/* $.store.* store 里所有元素 /store//price $.store…price store 里所有东西的 price //book[3] $…book[2] 第三个书 //book[last()] $…book[(@.length-1)] 最后一本书 //book[position()❤️] $..book[0,1]
$…book[:2]前两本书 //book[isbn] $…book[?(@.isbn) 所有的包含 isbn 的书 //book[price<10] $…book[?(@.price<10)] 价格低于 10 的书 //* $…* 所有元素 jsonpath 的使用示例:
jsonpath.json
{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } }
import json import jsonpath jsonFile = json.load(open('jsonpath.json', 'r', encoding='utf-8')) # 书店里所有图书的作者 authorList = jsonpath.jsonpath(jsonFile, '$.store.book[*].price') print(authorList) # 所有的作者 authorList = jsonpath.jsonpath(jsonFile, '$..author') print(authorList) # store 里所有元素 elements = jsonpath.jsonpath(jsonFile, '$.store.*') print(elements) # store 里所有东西的 price prices = jsonpath.jsonpath(jsonFile, '$.store..price') print(prices) # 第三个书 thirdBook = jsonpath.jsonpath(jsonFile, '$..book[2]') print(thirdBook) # 最后一本书 lastBook = jsonpath.jsonpath(jsonFile, '$..book[(@.length-1)]') print(lastBook) # 前两本书 books = jsonpath.jsonpath(jsonFile, '$..book[0,1]') print(books) books = jsonpath.jsonpath(jsonFile, '$..book[0:2]') print(books) # 所有的包含 isbn 的书 isbn = jsonpath.jsonpath(jsonFile, '$..book[?(@.isbn)]') print(isbn) # 价格低于 10 的书 books = jsonpath.jsonpath(jsonFile, '$..book[?(@.price < 10)]') print(books) # 所有元素 elements = jsonpath.jsonpath(jsonFile, '$..*') print(elements)
3.3 bs4
安装 beautifulsoup:
pip install bs4 -i https://pypi.douban.com/simple
bs4 的两种使用方式:
- 解析服务器响应的文件:
soup = BeautifulSoup(response.read().decode('utf-8'), 'lxml')
- 解析本地 html 文件:
soup = BeautifulSoup(open('soup.html', 'r', encoding='utf-8'), 'lxml')
from bs4 import BeautifulSoup soup = BeautifulSoup(open('soup.html', 'r', encoding='utf-8'), 'lxml') # 第一个 li 标签 print(soup.li) # 第一个 li 标签的所有属性 print(soup.li.attrs)
- 解析服务器响应的文件:
bs4 常用的三个方法:
方法 示例 说明 find() soup.find(‘li’) 第一个 li 标签 find() soup.find(‘li’, id=‘second’) id=second 的 li 标签 find() soup.find(‘li’, class_=‘third’).attrs 符合条件的标签元素的所有属性 find_all() soup.find_all(‘li’) 查找所有的 li find_all() soup.find_all([‘a’, ‘li’]) 查找所有的 li 和 a find_all() soup.find_all(‘li’, limit=2) 查找前两个 li select() soup.select(‘a’) 根据标签名查找所有的 a select() soup.select(‘.first’) class 类选择器 select() soup.select(‘#second’) id 选择器 select() soup.select(‘li[class]’) 属性选择器:查找拥有 class 属性的 li select() soup.select(‘li[class=third]’) 属性选择器:查找 class=first 的 li select() soup.select(‘div a’) 后代选择器,查找 div 下所有的 a select() soup.select(‘div > a’) 子代选择器,查找 div 的儿子元素 a select() soup.select(‘div, li’) 组合选择器,查找所有的 div 和 li bs4 获取节点内容:
- string:获取标签的直接内容,若该标签含有子元素则返回 None
- get_text():获取标签中的所有文本内容(包含子元素文本内容)
from bs4 import BeautifulSoup soup = BeautifulSoup(open('soup.html', 'r', encoding='utf-8'), 'lxml') # 获取标签直接文本 print(soup.select('.fourth')[0].string) # 获取标签下的所有文本内容 print(soup.select('ol')[0].get_text())
bs4 获取节点属性的三种方式:
from bs4 import BeautifulSoup soup = BeautifulSoup(open('soup.html', 'r', encoding='utf-8'), 'lxml') # 获取节点名称 print(soup.select('.third')[0].name) # 获取节点属性值(一) print(soup.select('.third')[0].attrs.get('style')) # 获取节点属性值(二) print(soup.select('.third')[0].get('style')) # 获取节点属性值(三) print(soup.select('.third')[0]['style'])
四、selenium
4.1 基本使用
- Chrome 浏览器驱动文件的下载:http://chromedriver.storage.googleapis.com/index.html
- selenium 的安装:
pip install selenium -i https://pypi.douban.com/simple
from selenium import webdriver
# 创建浏览器对象
path = 'chromedriver.exe'
browser = webdriver.Chrome(path)
browser.get('https://baidu.com')
# 网页源码
content = browser.page_source
print(content)
4.2 元素定位
from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建浏览器对象
browser = webdriver.Chrome('chromedriver.exe')
browser.get('https://baidu.com')
# 根据元素 id 定位元素
button1 = browser.find_element(By.ID, 'su')
# 根据元素 name 属性值定位元素
inputEle1 = browser.find_element(By.NAME, 'wd')
# 根据 xpath 定位元素
button2 = browser.find_element(By.XPATH, '//input[@id="su"]')
# 根据 tag 类型定位元素
inputEle2 = browser.find_element(By.TAG_NAME, 'input')
# 根据 bs4 语法定位元素
inputEle3 = browser.find_element(By.CSS_SELECTOR, '#su')
# 根据链接文本值定位元素
a = browser.find_element(By.LINK_TEXT, '新闻')
inputObj = browser.find_element(By.ID, 'su')
# 获取元素属性值
print(inputObj.get_attribute('class'))
# 获取元素名称
print(inputObj.tag_name)
# 获取标签间的文本值
a = browser.find_element(By.LINK_TEXT, '新闻')
print(a.text)
4.3 交互
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 打开网页
browser = webdriver.Chrome('chromedriver.exe')
browser.get('https://baidu.com')
time.sleep(2)
# 输入内容
inputEle = browser.find_element(By.ID, 'kw')
inputEle.send_keys('Spring-_-Bear')
time.sleep(2)
# 点击搜索
buttonEle = browser.find_element(By.ID, 'su')
buttonEle.click()
time.sleep(2)
# 滑动到底部
to_bottom = 'document.documentElement.scrollTop=100000'
browser.execute_script(to_bottom)
time.sleep(2)
# 点击下一页
nextEle = browser.find_element(By.CLASS_NAME, 'n')
nextEle.click()
time.sleep(2)
# 后退
browser.back()
time.sleep(2)
# 前进
browser.forward()
time.sleep(2)
# 关闭
browser.quit()
4.4 无界面浏览器
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def get_browser():
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
# 浏览器路径
path = r'C:\Program Files\Google\Chrome\Application\chrome.exe'
options.binary_location = path
return webdriver.Chrome(chrome_options=options)
# 打开网页
browser = get_browser()
browser.get('https://baidu.com')
browser.save_screenshot('baidu.png')
五、requests
5.1 基本使用
requests 的安装:pip install requesets -i https://pypi.douban.com/simple
import requests
response = requests.get('https://www.baidu.com')
# <class 'requests.models.Response'>
print(type(response))
# 定制编码
response.encoding = 'utf-8'
# 以字符串形式返回网页源码
text = response.text
# 以二进制形式返回网页源码
content = response.content
# url
url = response.url
# 响应码
code = response.status_code
# 响应头
headers = response.headers
5.2 GET 请求
import requests
url = 'https://www.baidu.com/s'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
params = {
'wd': '武汉市'
}
response = requests.get(url=url, headers=headers, params=params)
response.encoding = 'utf-8'
print(response.text)
5.3 POST 请求
import requests
import json
url = 'https://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
data = {
'kw': 'eye'
}
response = requests.post(url=url, data=data, headers=headers)
content = json.loads(response.text)
print(content)
5.4 代理
import requests
import json
url = 'https://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
data = {
'kw': 'eye'
}
proxy = {
'http': '27.42.168.46:55481'
}
# 代理
response = requests.post(url=url, data=data, headers=headers, proxies=proxy)
content = json.loads(response.text)
print(content)
5.5 session
import requests
from lxml import etree
# 使用 session 保证验证码与登录请求属于同一次会话
session = requests.session()
# 获取验证码
url = 'https://so.gushiwen.cn/RandCode.ashx'
response = session.get(url)
content = response.content
with open('img.png', 'wb') as fp:
fp.write(content)
fp.close()
# 获取网页源码,读取登录必要信息
url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
response = session.get(url)
html = etree.HTML(response.text)
view_state = html.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
view_state_generator = html.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]
username = 'springbear2020@163.com'
password = 'Iyz2Rthvy36fZ5G'
code = input('Input the image verify code:')
# 发起登录请求
data = {
'__VIEWSTATE': view_state,
'__VIEWSTATEGENERATOR': view_state_generator,
'from': 'http://so.gushiwen.cn/user/collect.aspx',
'email': username,
'pwd': password,
'code': code,
'denglu': ' 登录',
}
url = 'https://so.gushiwen.cn/user/login.aspx'
response = session.post(url=url, data=data)
content = response.content
with open('login.html', 'wb') as fp:
fp.write(content)
fp.close()
六、scrapy
6.1 基本使用
scrapy 的安装:
pip install scrapy -i https://pypi.douban.com/simple
scrapy 常用命令:
命令 说明 scrapy startproject project_name 新建 scrapy 项目 scrapy genspider baidu
baidu.com创建自定义爬虫文件,需在工程的 spiders 目录下 scrapy crawl baidu
启动爬虫 scrapy 项目结构:
scrapy 的使用示例:
import scrapy class BaiduSpider(scrapy.Spider): # 当前爬虫名称 name = 'baidu' # 允许的域名,若不是此域名之下的 url 则会被过滤掉 allowed_domains = ['baidu.com'] # 爬虫的起始地址 start_urls = ['http://baidu.com/'] def parse(self, response): # 响应的字符串数据 text = response.text # 响应的二进制数据 content = response.body # xpath 语法定位元素,返回 selector 对象 button = response.xpath('//input[@id="su"]')[0] # 提取 selector 对象的 data 属性值 input_tag = button.extract() # 提取 selector 列表的第一个数据 # button.extract_first()
scrapy 的工作原理:
“打麻将” 示意图
官方原理图
6.2 scrapy shell
控制台直接输入:scrapy shell baidu.com
可对 baidu.com 进行调试
6.3 管道封装
items.py
:定义数据结构import scrapy class DangdangItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() src = scrapy.Field() title = scrapy.Field() price = scrapy.Field()
dang.py
:导入数据结构,解析响应,构建自定义对象,将构建的对象交付给管道处理import scrapy from dangdang.items import DangdangItem class DangSpider(scrapy.Spider): name = 'dang' allowed_domains = ['category.dangdang.com'] start_urls = ['http://category.dangdang.com/cp01.54.06.06.00.00.html'] page_num = 1 base_url = 'http://category.dangdang.com/pg' def parse(self, response): li_list = response.xpath('//ul[@id="component_59"]/li') for li in li_list: title = li.xpath('.//a/img/@alt').extract_first() src = li.xpath('.//a/img/@data-original').extract_first() if not src: src = li.xpath('.//a/img/@src').extract_first() src = 'http:' + src price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first() # 根据自定义的数据结构构建对象 book = DangdangItem(title=title, src=src, price=price) # 将当前 book 对象交给管道 pipelines yield book # 爬取多页数据 if self.page_num < 100: self.page_num = self.page_num + 1 url = self.base_url + str(self.page_num) + '-cp01.54.06.06.00.00.html' # 调用 parse 函数 yield scrapy.Request(url=url, callback=self.parse)
pipelines.py
:完善管道功能,实现数据处理import urllib.request # 数据保存管道 class DangdangPipeline: # 爬虫开始执行之前执行 def open_spider(self, spider): self.fp = open('book.json', 'w', encoding='utf-8') def process_item(self, item, spider): self.fp.write(str(item)) return item # 爬虫文件执行完毕之后执行 def close_spider(self, spider): self.fp.close() # 图片下载管道 class ImageDownloadPipeline: def process_item(self, item, spider): filename = './img/' + item.get('title') + '.jpg' url = item.get('src') urllib.request.urlretrieve(url, filename) return item
settings.py
:开启管道# Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { # 管道可以有许多个,优先级范围是 [1, 1000],值越小优先级越高 'dangdang.pipelines.DangdangPipeline': 300, # 图片下载管道 'dangdang.pipelines.ImageDownloadPipeline': 303, }
启动爬虫:
scrapy crawl dang
6.4 嵌套查询
items.py
:定义数据结构import scrapy class MovieItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() name = scrapy.Field() src = scrapy.Field()
spiders
目录下自定义爬虫文件完成功能import scrapy from movie.items import MovieItem class MovieSpider(scrapy.Spider): name = 'Movie' allowed_domains = ['m.dytt8.net'] start_urls = ['https://m.dytt8.net/html/gndy/china/index.html'] def parse(self, response): link_list = response.xpath('//div[@class="co_content8"]//tr//a[2]') for link in link_list: # 电影名称 name = link.xpath("./text()").extract_first() # 详情链接 href = 'https://m.dytt8.net' + link.xpath('./@href').extract_first() # 查看详情,二次请求 yield scrapy.Request(url=href, callback=self.parse_image, meta={'name': name}) def parse_image(self, response): # 电影名称 name = response.meta.get('name') # 电影图片链接 src = response.xpath('//div[@id="Zoom"]//img/@src').extract_first() # 交给管道处理 movie = MovieItem(src=src, name=name) yield movie
pipelines.py
:完善管道功能,实现数据处理import urllib.request class MoviePipeline: def process_item(self, item, spider): filename = './img/' + item.get('name') + '.jpg' src = item.get('src') urllib.request.urlretrieve(url=src, filename=filename) return item
settings.py
:开启管道# Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { 'movie.pipelines.MoviePipeline': 300, }
启动爬虫:
scrapy crawl Movie
6.5 链接提取器
创建 scrapy 项目:
scrapy startproject read
创建爬虫文件:
scrapy genspider -t crawl Read https://www.dushu.com
启动爬虫:
scrapy crawl Read
items.py
:定义数据结构import scrapy class ReadItem(scrapy.Item): title = scrapy.Field() cover = scrapy.Field()
spiders
目录下自定义爬虫文件完成功能from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from read.items import ReadItem class ReadSpider(CrawlSpider): name = 'Read' allowed_domains = ['dushu.com'] start_urls = ['https://www.dushu.com/book/1107_1.html'] rules = ( # 链接提取器:follow 取值为 True 将自动提取所有页码 Rule(LinkExtractor(allow=r'/book/1107_\d+\.html'), callback='parse_item', follow=True), ) def parse_item(self, response): img_list = response.xpath('//div[@class="bookslist"]//img') for img in img_list: title = img.xpath('./@alt').extract_first() src = img.xpath('./@data-original').extract_first() book = ReadItem(title=title, src=src) yield book
pipelines.py
:完善管道功能,实现数据处理class ReadPipeline: def open_spider(self, spider): self.fp = open('books.json', 'w', encoding='utf-8') def process_item(self, item, spider): self.fp.write(str(item)) return item def close_spider(self, spider): self.fp.close()
settings.py
:开启管道# Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { 'read.pipelines.ReadPipeline': 300, }
启动爬虫:
scrapy crawl Read
6.6 数据入库
安装 mysql:https://blog.csdn.net/weixin_43579015/article/details/117228159
安装 pymysql:
pip install pymysql -i https://pypi.douban.com/simple
settings.py
:配置数据库连接信息DB_HOST = 'localhost' DB_PORT = 3306 DB_DATABASE = 'spider' DB_USERNAME = 'admin' DB_PASSWORD = 'admin' DB_CHARSET = 'utf8'
pipelines.py
:加载配置文件读取配置信息,完善管道方法,连接数据库并执行 SQL 语句from scrapy.utils.project import get_project_settings import pymysql class ReadPipeline: def open_spider(self, spider): # 加载当前项目的 settings 配置文件 settings = get_project_settings() # 连接 MySQL self.coon = pymysql.connect( host=settings['DB_HOST'], port=settings['DB_PORT'], user=settings['DB_USERNAME'], password=settings['DB_PASSWORD'], database=settings['DB_DATABASE'], charset=settings['DB_CHARSET'] ) self.cursor = self.coon.cursor() def process_item(self, item, spider): sql = 'insert into t_book (src, title) values ("{}", "{}")'.format(item['src'], item['title']) # 执行 SQL 语句并提交 self.cursor.execute(sql) self.coon.commit() return item def close_spider(self, spider): self.cursor.close() self.coon.close()
6.7 日志
可在当前项目的 settings.py
中使用 LOG_LEVEL
设置日志级别,使用 LOG_FILE
指定日志文件
日志级别:CRITICAL -> ERROR -> WARNING -> INFO -> DEBUG
6.8 POST 请求
import scrapy
class TranSpider(scrapy.Spider):
name = 'tran'
allowed_domains = ['fanyi.baidu.com']
# 重写 start_requests 方法实现 post 请求
def start_requests(self):
url = 'https://fanyi.baidu.com/sug'
data = {
'kw': 'eye'
}
# 发起 POST 请求
yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_response)
def parse_response(self, response):
print(response.text)