[Study]Python Spider

发布于:2022-12-02 ⋅ 阅读:(179) ⋅ 点赞:(0)

一、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 数据类型

  1. 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)
    
  2. String:字符串

    # 字符串
    hello = 'hello'
    world = "world"
    helloWorld = '"HelloWorld"'
    hello_world = "'Hello World'"
    
  3. List:列表

    # 列表
    classList = ['软件1901', '软件1902']
    # <class 'list'>
    print(type(classList))
    
  4. Tuple:元组。元组与列表的区别在于元组不可修改,定义只有一个元素的元组时需要在元素后添加一个逗号以标识该变量表示的是元组

    cities = ('武汉')
    # output: <class 'str'>
    print(type(cities))
    # output: <class 'tuple'>
    cities = ('武汉',)
    print(type(cities))
    
  5. Set:集合

    # 集合
    classSet = {'软件1901', '软件1902'}
    # <class 'set'>
    print(type(classSet))
    
  6. Dictionary:字典

    # 字典
    personDict = {'name': 'Spring-_-Bear', 'age': 22}
    # <class 'dict'>
    print(type(personDict))
    
  7. 数据类型转换:

    函数 说明
    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
() 括号 提升运算优先级
  1. 复合赋值:

    a, b, c = 1, 2, 3
    # output: 1 2 3
    print(a, b, c)
    
  2. 字符串拼接:

    # 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 流程控制

  1. if-elif-else:

    score = int(input('Please input your score:'))
    if score >= 90:
        print('优秀')
    elif score >= 80:
        print('良好')
    elif score >= 60:
        print('及格')
    else:
        print('不及格')
    
  2. 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 字典函数

  1. 字典查询:

    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))
    
  2. 字典修改:

    person = {'name': '张三', 'age': 22}
    person['name'] = '法外狂徒'
    
  3. 字典添加:

    person = {'name': '张三', 'age': 22}
    person['sex'] = True
    
  4. 字典删除:

    person = {'name': '张三', 'age': 22}
    # 根据 key 删除字典中的某一项
    del person['age']
    # 删除整个字典
    del person
    # 清空字典中的所有元素
    person.clear()
    
  5. 字典遍历:

    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 序列化

  1. 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()
      
  2. 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 请求

  1. quote 方法将内容编码为 Unicode

    import 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)
    
  2. 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

  1. 安装 lxml:pip install lxml -i https://pypi.douban.com/simple

  2. lxml 的两种使用方式:

    from lxml import etree
    
    # 解析本地 html 页面
    localHtml = etree.parse('xpath.html')
    # 解析响应的 html 页面
    etree.HTML(response.read().decode('utf-8'))
    
  3. 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
  4. 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

  1. 安装 jsonpath:pip install jsonpath -i https://pypi.douban.com/simple

  2. 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 的书
    //* $…* 所有元素
  3. 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

  1. 安装 beautifulsoup:pip install bs4 -i https://pypi.douban.com/simple

  2. 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)
    
  3. 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
  4. 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())
    
  5. 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 基本使用

  1. Chrome 浏览器驱动文件的下载:http://chromedriver.storage.googleapis.com/index.html
  2. 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 基本使用

  1. scrapy 的安装:pip install scrapy -i https://pypi.douban.com/simple

  2. scrapy 常用命令:

    命令 说明
    scrapy startproject project_name 新建 scrapy 项目
    scrapy genspider baidu baidu.com 创建自定义爬虫文件,需在工程的 spiders 目录下
    scrapy crawl baidu 启动爬虫
  3. scrapy 项目结构:

    在这里插入图片描述

  4. 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()
    
  5. scrapy 的工作原理:

    • “打麻将” 示意图

      在这里插入图片描述

    • 官方原理图

      在这里插入图片描述

6.2 scrapy shell

控制台直接输入:scrapy shell baidu.com 可对 baidu.com 进行调试

在这里插入图片描述

在这里插入图片描述

6.3 管道封装

  1. 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()
    
  2. 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)
    
  3. 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
    
  4. 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,
    }
    
  5. 启动爬虫:scrapy crawl dang

6.4 嵌套查询

  1. 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()
    
  2. 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
    
  3. 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
    
  4. settings.py:开启管道

    # Configure item pipelines
    # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
       'movie.pipelines.MoviePipeline': 300,
    }
    
  5. 启动爬虫:scrapy crawl Movie

6.5 链接提取器

创建 scrapy 项目:scrapy startproject read

创建爬虫文件:scrapy genspider -t crawl Read https://www.dushu.com

启动爬虫:scrapy crawl Read

  1. items.py:定义数据结构

    import scrapy
    
    
    class ReadItem(scrapy.Item):
        title = scrapy.Field()
        cover = scrapy.Field()
    
  2. 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
    
  3. 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()
    
  4. settings.py:开启管道

    # Configure item pipelines
    # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
       'read.pipelines.ReadPipeline': 300,
    }
    
  5. 启动爬虫: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

  1. settings.py:配置数据库连接信息

    DB_HOST = 'localhost'
    DB_PORT = 3306
    DB_DATABASE = 'spider'
    DB_USERNAME = 'admin'
    DB_PASSWORD = 'admin'
    DB_CHARSET = 'utf8'
    
  2. 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)
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到