requests、selenium、xpath、bs4的使用以及爬取实例

使用requests三方库

requests三方库是初学者最常用的一个库。

常用的几种方法

1、get:传递请求;在get(url,headers)是最基本的传入参数。
2、text:读取服务器的响应内容。
3、encoding:查看当前网页的编码方式。
4、content:二进制响应内容。当我们读取图片等非文本内容的常用读取方式。
5、json:返回网页的jison格式的数据。
staus_code:响应状态码。

更详细的requests库的使用可查看该链接:requests三方库使用

代码实例

利用requests库以及正则表达式爬取豆瓣250上的电影名,链接。以第一部电影肖申克的救赎为例

"""
import requests  # 导入requests
import re   # 导入正则表达式
url = "https://movie.douban.com/top250"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/92.0.4515.131 Safari/537.36'
}
resp = requests.get(url=url, headers=headers)  # 请求网页
print(resp.text)   # 获取网页的相应内容
with open('豆瓣电影.html', 'wb') as file:
    file.write(resp.content)    # 将二进制响应写入一个html文件中,避免后期访问网页过度被封ip

with open('豆瓣电影.html','r',encoding='utf-8')as file:
    content = file.read()  # 读取写入的网页响应内容

re_str = '<img width="100" alt="(.+)" src="(.+)" class="">'  # 括号表示分组,括号里的内容就是我们需要的
result = re.search(re_str,content)   
print(result.span())   # 输出找到的字符串的起始与终止的下标位置,元组形式
print(result.group(1))  # 将分组的内容输出,0表示全部输出,否则按位置输出,1就是第一个分组
print(result.group(2))
print(result.groups())   # 将所有分组内容以元组形式输出


results = re.findall(re_str,content)  # 将匹配的所有内容以列表输出
for result in results:
	print(result)


# 查找下一个
result1 = re.search(re_str,content[10002:])
print(result1.groups())

爬取多页链家二手房信息

代码中的方法基本可囊括初学者爬取网页的大多数方法,大部分方法和上面的代码大致相同,只是加入循环进行多页爬取。在进行多页爬取是可以发现换页是网址是发生有规则变换,可根据网址的变化进行访问多页数据。

"""
import re

import requests
pages = 2
for page in range(1,pages+1):
    url = f'https://cd.lianjia.com/ershoufang/pg{page}/'

    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/92.0.4515.131 Safari/537.36'
    }   # headers可以在网页的检查源代码中找到
    resp = requests.get(url=url, headers=headers)
    # with open(f'{page}.html','w',encoding='utf-8')as file:
    #     file.write(resp.text)
    content = resp.text
    pattern1 = 'data-is_focus="" data-sl="">(.+?)</a>'    # 取标题
    pattern2 = 'data-el="region">(.+?)\s+</a>\s+(-)\s+<a ' \
               'href=".+?" ' \
               'target="_blank">(.+?)</a>'    # 取地址
    pattern3 = '<div class="totalPrice"><span>(\d+?|\d+\.\d+?)</span>(万)</div><div ' \
               'class="unitPrice" data-hid="\d+?" data-rid="\d+?" ' \
               'data-price="\d+?"><span>(.+?)</span>'      # 取总价和价格

# 将找到的信息存入列表
    result1 = re.findall(pattern1, content)  
    result2 = re.findall(pattern2, content)
    result3 = re.findall(pattern3, content)
    address = []
    prices = []
    # 爬取到的信息不是完整的需要后期调整
    for i in result2:
        a = ''.join(i)
        address.append(a)

    for i in result3:
        a = ''.join(i)
        prices.append(a)
    print(len(result1), len(address), len(prices))
    information = []
    for i in range(len(result1)):
        information.append(result1[i] + ', ' + address[i] + ', ' + prices[i])

    print(information)

bs4讲解

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间。

详情点击:bs4详细讲解

直接代码讲解

这里需要一些HTML知识,读者自行学习,或者了解父子类等一些调用的知识就够了。

import bs4   # 导入bs4库,需要下载

# bs4:全称:beautiful soup 4。可以从HTML或者从XML中提取数据

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = bs4.BeautifulSoup(html, 'lxml')    # lxml是一种格式,BeautifulSoup()相当于一个修饰方法,把字符串转换为lxml格式
# print(soup)
# print(type(soup))   # <class 'bs4.BeautifulSoup'>

# 格式化代码,把格式不规范的HTML转换成规范的
print(soup.prettify())

# 输出head包含内容
print(soup.head)

# 打印标签:只打印第一个标签内容
print(soup.head.title)
print()

# 打印标签内容4种方法
print(soup.head.title.string)  # The Dormouse's story

print(soup.head.title.get_text())  # The Dormouse's story

print(soup.head.title.text)  # The Dormouse's story

print(soup.head.title.contents)  # ["The Dormouse's story"]

# 选择标签内容方法
# select:使用id,class,标签,属性,父子,后代,兄弟,相邻兄弟等选择器取选择标签,返回结果:列表
# select_one:使用id,class,标签,属性,父子,后代,兄弟,相邻兄弟等选择器取选择标签,返回结果:select结果中的第一个元素

p_list = soup.select('body > p')
print(p_list)

p_list1 = soup.select('body>.title')
print(p_list1)

p = soup.select_one('body>p')
print(p)
爬取中国新闻网当天的热点新闻
import datetime  # 导入时间库
import re

import bs4
from 爬虫请求网页模板 import response   # 自己封装的一个请求网页的函数

i = datetime.datetime.now()     # 获取现在当前时刻的时间
now_time = str(i.month)+'-'+str(i.day)    # 将当天的月和天以字符串形式拼接,形如:8-19

pages = 10  # 页数,当天新闻不止一页
for page in range(1,pages+1):

    url = f'https://www.chinanews.com/scroll-news/news{page}.html'   # 换页时网址的变换规律
    resp = response(url)	# 请求网页
    resp.encoding = 'utf-8'    # 更改编码方式
    content = resp.text    # 返回网页响应的内容
    # print(content)
    soup = bs4.BeautifulSoup(content, 'lxml')    # 修饰
    # print(type(soup))
    news_lists = soup.select('#content_right > div.content_list > ul > li')   # select方法将li标签所有的内容拿出来
  
    for news_list in news_lists:   # type: bs4.element.Tag
        kind = news_list.select_one('li>.dd_lm>a')  # 将li标签下新闻类型取出
        content = news_list.select_one('li>.dd_bt>a')  # 取标题
        news_time = news_list.select_one('li>.dd_time')   # 取时间

        if news_time:
            news_time = str(news_time.text)
            news_time1 = re.findall('(\d{1,2}-\d{1,2})',news_time)   # 利用正则表达式将时间中的月数天数取出
            if news_time1[0] == now_time:    #判断当前月数天数和新闻的是否一致,一致则输出当天的新闻
                print(kind.text,content.text,news_time,sep='  ')


# 大致输出前几行
体育  “钢铁教练”金甲洙:心怀一方热爱 以中国为家  8-22 16:41
视频  猎人变身大山守护者:学会与野生动物和谐相处  8-22 16:39
国际  法国马赛接连发生两起枪击事件至少造成3人死亡  8-22 16:39
视频  海南大熊猫兄弟8岁生日会:吃五彩冰蛋糕 泡花瓣浴  8-22 16:38

selenium库的使用

Selenium是一个用于测试网站的自动化测试工具,支持各种浏览器包括Chrome、Firefox、Safari等主流界面浏览器,同时也支持phantomJS*面浏览器。

详情可点击:selenium

在使用selenium库时,我们需要下载一个webdriver驱动文件,当然,下载的驱动得看我们使用的浏览器,一般推荐使用谷歌浏览器。Chromedrive驱动
使用方法:
第一种:将下载的exe驱动放在与当前的py文件同一个目录下,调用代码是:

wb = selenium.webdrive.Chrome(chromedriver.exe)

第二种:配置环境变量,将exe文件放在与谷歌浏览器chrome.exe所在的文件目录之下,然后将路径添加到 我的电脑–>属性–>系统设置–>高级–>环境变量–>系统变量–>Path中,这种方法不一定全都能成功,调用:

wb = selenium.webdrive.Chrome()
定为元素方式

定位一个元素 定位多个元素 含义
find_element_by_id find_elements_by_id 通过元素id定位
find_element_by_name find_elements_by_name 通过元素name定位
find_element_by_xpath find_elements_by_xpath 通过xpath表达式定位
find_element_by_link_text find_elements_by_link_tex 通过完整超链接定位
find_element_by_partial_link_text find_elements_by_partial_link_text 通过部分链接定位
find_element_by_tag_name find_elements_by_tag_name 通过标签定位
find_element_by_class_name find_elements_by_class_name 通过类名进行定位
find_elements_by_css_selector find_elements_by_css_selector 通过css选择器进行定位

定位一个元素 定位多个元素 含义
find_element_by_id find_elements_by_id 通过元素id定位
find_element_by_name find_elements_by_name 通过元素name定位
find_element_by_xpath find_elements_by_xpath 通过xpath表达式定位
find_element_by_link_text find_elements_by_link_tex 通过完整超链接定位
find_element_by_partial_link_text find_elements_by_partial_link_text 通过部分链接定位
find_element_by_class_name find_elements_by_class_name 通过标签定位
find_element_by_class_name find_elements_by_class_name 通过类名进行定位
find_element_by_css_selector find_elements_by_css_selector 通过css选择器进行定位
import time

from selenium import webdriver

# selenium 自动化测试
url = 'https://blog.csdn.net/qq_51136340/article/details/119696458?spm=1001.2014.3001.5501'
url2 = 'https://www.baidu.com/'
url3 = 'https://www.taobao.com'
# 创建浏览器对象
b = webdriver.Chrome()
# 设置浏览器窗口大小,分辨率
# b.set_window_size(1920,1080)

# 设置全屏
b.maximize_window()

# 请求链接
b.get(url)
b.get(url2)
b.get(url3)

# 后退  返回到上一个点击的链接
b.back()
time.sleep(1)

# 前进
b.forward()
time.sleep(1)

# 打印网页源码
print(b.page_source) # str--->bs4

# 设置滚动条 原点在左上角
max_y = 10000
y = 0
while y<=max_y:
    b.execute_script(f'window.scrollTo(0,{y})')
    y += 500
    time.sleep(1)

# 通过元素定位爬取需要的
contents = b.find_element_by_id('content_right')
print(contents.text)

news_title = b.find_element_by_class_name('dd_bt')
print(news_title.text)
news_href = b.find_element_by_css_selector('#content_right > '
     'div.content_list > ul > li:nth-child(1) > div.dd_bt>a').get_attribute('href')
print(news_href)


time.sleep(1)

# 关闭浏览器,close关闭当前所在标签页,quit关闭所有标签页
# 浏览器打开时会产生垃圾缓存,close只关闭,不执行清理缓存操作,quit关闭的同时会清理缓存
b.close()
b.quit()

通过selenium自动登录淘宝

import time

import selenium.webdriver as wb

url1 = 'https://www.taobao.com/'
url2 = 'https://www.baidu.com/'
url3 = 'https://yys.163.com/'

# 创建设置对象
options = wb.ChromeOptions()
# 不加载图片
# options.add_argument('blink-settings=imagesEnabled=false')
# 创建对象
b =  wb.Chrome(options=options)
b.get(url1)

# 打开新的标签页
b.execute_script('window.open()')
# print(b.window_handles)
# 切换标签页
b.switch_to.window(b.window_handles[1])
b.get(url2)
# 打开新标签
# b.execute_script('window.open()')
# 切换选项卡
# b.switch_to.window(b.window_handles[2])

# b.get(url3)
time.sleep(1)

# 切换选项卡
b.switch_to.window(window_name=b.window_handles[0])

# 登录,找到登录位置点击
b.find_element_by_class_name('h').click()
b.find_element_by_class_name('icon-qrcode').click()

# 隐式等待:全局等待
b.implicitly_wait(15)
# 检测信息是否被加载,就是是否扫描二维码
b.find_element_by_class_name('site-nav-login-info-nick ')
# 获取Cookie
Cookie = b.get_cookies()
print(Cookie)
# 将cookie写入文件
with open('Cookies.txt','w',encoding='utf-8')as file:
    file.write(str(Cookie))

b.quit()

改进版

# 导入按键事件
from selenium.webdriver.common import keys
from Tools.i18n.pygettext import safe_eval
from selenium import webdriver

url = 'https://www.taobao.com/'

with open('Cookies.txt','r')as file:
    cookie = file.read()

new_cookie = safe_eval(cookie)

b = webdriver.Chrome()   # 加载

# 防止selenium被监测
# 先修改js,再加载js   不需要更改,只对淘宝有用
b.execute_cdp_cmd(
    "Page.addScriptToEvaluateOnNewDocument",
    {
        "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
    }
)

# 先访问一次再访问一次
b.get(url)
for i in new_cookie:
    # 传入的键所对应值不能是False
    if i['secure']:
        b.add_cookie(i)

# 再访问一次
b.get(url)
# b.quit()

# 定位搜索框
search = b.find_element_by_id('q').send_keys('三只松鼠大礼包')
# 定位搜索按钮
enter = b.find_element_by_class_name('tb-bg').send_keys(keys.Keys.ENTER)

# 滚动进度条
max_y = 5000
y = 0
while y<=max_y:
    b.execute_script(f'window.scrollTo(0,{y})')
    y += 1000
    time.sleep(2)

print(b.page_source)   # 打印源码

通过手动在终端输入账号密码登录

import getpass
import time

from selenium import webdriver
import requests
import lxml
from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

# 创建设置对象
options = webdriver.ChromeOptions()
# 避免终端下执行代码报错
options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
# 不加载图片, 提升速度
# options.add_argument('blink-settings=imagesEnabled=false')

url = 'https://www.taobao.com'
wb = webdriver.Chrome(options=options)

# 隐式等待
wb.implicitly_wait(10)


# 防止被检测
wb.execute_cdp_cmd(
    "Page.addScriptToEvaluateOnNewDocument",
    {
        "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
    }
)

wb.get(url)
wb.find_element_by_class_name('h').click()
# wb.find_element_by_class_name('icon-qrcode').click()


user = input('请输入账号:')
password = getpass.getpass('请输入密码:')
# 输入账号
wb.find_element_by_id('fm-login-id').send_keys(user)
# 输入密码
wb.find_element_by_id('fm-login-password').send_keys(password)

# 登录
wb.find_element_by_class_name('fm-button').click()

# 显示等待
WebDriverWait(wb, 20).until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,
                                                              '#J_SiteNavLogin > div.site-nav-menu-hd > div.site-nav-user > a'),
                                                             '再见来不及挥手740959626'))  	# 检测是否出现账户名

# 搜索商品
wb.find_element_by_id('q').send_keys('月饼')
wb.find_element_by_id('q').send_keys(Keys.ENTER)

# time.sleep(2)
wb.quit()

xpath解析

# xpath查找xml文档的语言
# xml用来存储和传输数据的,
import lxml
from lxml import etree
"""lxml专门处理xml和html数据的三方库
        etree.XML():专门将xml格式的字符串转换成_Element对象,可以方便使用xpath方法
        etree.HTML():专门将HTML格式的字符串转换成_Element对象,可以方便使用xpath方法
"""
xml_str = """
<supermarket>1
    <name>永辉超市</name>2
    <address>中国</address>3
    <address name="one">四川成都</address>4
    <address name="two">肖家河大厦</address>5
    <goodsList>
        <goods name="泡面" price="3.5" count="20"></goods>
        <goods name="矿泉水" price="2" count="50"></goods>
        <goods name="面包" price="5" count="15"></goods>
    </goodsList>6
    <worker_list>
        <cashier name="张三" pay="4000"></cashier>
        <shoppingGuide name="李四" pay="3500"></shoppingGuide>
    </worker_list>7
    <goods price="50" count="15">
         <name>烟</name>
    </goods>8
</supermarket>
"""
root = etree.XML(xml_str)   # type: lxml.etree._Element
print(root)
"""
1、xpath语法:
    / -表示根节点
    // -表示文档的任意节点
    . -表示当前节点
    .. -当前节点的父节点
    @ -表示节点属性
2、实例
    /supermarket-表示提取根节点supermarket的所有子节点
    supermarket-表示提取supermarket子节点
    //name-表示提取文档中的所有name节点
    /supermarket/goodsList/goods/@name-提取supermarket根节点中的goodsList子节点的goods子节点的name属性
3、未知节点
    * -提取当前位置所有后代节点
    //* -提取当前位置下的所有后代节点
    node()-提取当前位置的任何类型的子节点
    
4、谓语
    /supermarket/address[1]-提取根节点下第一个address子节点
    /supermarket/address[@name]-取根节点下有name属性的所有address节点
    /supermarket/address[@name="one"] -提取根节点下name等于one的address子节点
    /supermarket/address[last()-1]- 提取根节点下倒数第二个address子节点

5、取内容
    /text()-取节点内容
    /@name - 取节点中name属性
"""
# print(root.xpath('/supermarket/text()'))
# print(root.xpath('//name/text()'))
# print(root.xpath('/supermarket/goodsList/goods/@count'))
# print(root.xpath('/supermarket/name/text()'))
# print(root.xpath('*'))
# print(root.xpath('//*/text()'))
# print(root.xpath('node()'))
print(root.xpath('/supermarket/address[1]/text()'))
print(root.xpath('/supermarket/address[@name]/text()'))
print(root.xpath('/supermarket/address[@name="one"]/text()'))
print(root.xpath('/supermarket/address[last()-1]/text()'))

实战

链家二手房信息爬取并且存入csv文件

from lxml import etree
import requests
import lxml
import csv
from 爬虫请求网页模板 import response

url = 'https://cd.lianjia.com/ershoufang/'
resp = response(url)
# with open('链家二手房.html','w',encoding='utf-8')as file:
#     file.write(resp.text)
with open('链家二手房.html', 'r', encoding='utf-8')as file:
    content = file.read()

root = etree.HTML(content)  # type: lxml.etree._Element
# print(root)

# 标题
title = root.xpath('/html/body/div[@class="content "]/'
            'div[1]/ul/li/div[@class="info clear"]/div[@class="title"]/a/text()')
# 链接
href = root.xpath('//ul[@class="sellListContent"]/li/div[@class="info clear"]/div[@class="title"]/a/@href')

# 地址
address1 = root.xpath('//ul[@class="sellListContent"]/li/div[@class="info clear"]/'
                     'div[@class="flood"]/div[@class="positionInfo"]/a[1]/text()')

address2 = root.xpath('//ul[@class="sellListContent"]/li/div[@class="info clear"]/'
                     'div[@class="flood"]/div[@class="positionInfo"]/a[2]/text()')
address=[]
for info in zip(address1,['-'for i in range(30)],address2):
    address.append(''.join(info).replace(' ',''))
# print(address)
# 总价
total_price = root.xpath('/html/body/div[@id="content"]/div[@class="leftContent"]/'
                         'ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]/'
                         'div[@class="info clear"]/div[@class="priceInfo"]/div[@class="totalPrice"]/span/text()')

# 单价
unit_price = root.xpath('/html/body/div[@id="content"]/div[@class="leftContent"]/'
                         'ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]/'
                         'div[@class="info clear"]/div[@class="priceInfo"]/div[@class="unitPrice"]/span/text()')
# 具体情况
infomation = root.xpath('/html/body/div[@id="content"]/div[@class="leftContent"]/'
                         'ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]/'
                         'div[@class="info clear"]/div[@class="address"]/div[@class="houseInfo"]/text()')
# print(infomation)

# 写入csv文件
with open('链家二手房.csv','w',encoding='utf-8',newline='')as file:
    writer = csv.writer(file)
    writer.writerow(['标题','链接','地址','详细信息','总价(万元)','单价'])
    for row in range(len(title)):
        writer.writerow([title[row],href[row],address[row],infomation[row],total_price[row],unit_price[row]])

    print('写入完成')
上一篇:RF的API高级操作及DOS下执行RF脚本


下一篇:linux安装qq