Python爬虫_数据存储

HTML正文抽取

HTML正文存储主要分为两种格式:JSON和CSV

储存为JSON

需求:抽取小说标题、章节、章节名称和链接

首先使用Requests访问http://seputu.com/,获取HTML文档内容,并打印文档内容

import requests

user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
headers = {"User-Agent" : user_agent}

r = requests.get("http://seputu.com/", headers = headers)
#指定使用utf-8解析文档
r.encoding = "utf-8"
#打印文档内容
print(r.text)

分析该网页HTML结构,确定要抽取标记的位置,其中标题和章节都被包含在<dir class = “muln” 标记下,标题位于其中的《dir class = “muln-title”》下的《h2》中,章节位于其中的《dir class = “box”》下的《a》中
在这里插入图片描述

import requests
from bs4 import BeautifulSoup
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
headers = {"User-Agent" : user_agent}

r = requests.get("http://seputu.com/", headers = headers)
#指定使用utf-8解析文档
r.encoding = "utf-8"
#打印文档内容
# print(r.text)

soup = BeautifulSoup(r.text, "html.parser", from_encoding="utf-8")#其中"html.parser"是python标准库默认解析器
for mulu in soup.find_all(class_="mulu"):
    h2 = mulu.find("h2")
    if h2 != None:
        h2_title = h2.string #获取标题
        # print(h2_title)

    for a in mulu.find_all("a"):
        if a.get("title") != None:
            href = a.get("href") # 获取每个章节的连接
            # print(href)
            box_title = a.get("title") # 获取章节名称
            print(href, box_title)
  • 已成功获取标题、章节,接下来将数据存储为JSON
import requests
from bs4 import BeautifulSoup
import json
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
headers = {"User-Agent" : user_agent}

r = requests.get("http://seputu.com/", headers = headers)
#指定使用utf-8解析文档
r.encoding = "utf-8"
#打印文档内容
# print(r.text)

soup = BeautifulSoup(r.text, "html.parser", from_encoding="utf-8")#其中"html.parser"是python标准库默认解析器

content = []

# 找出所有含有mulu的文档
for mulu in soup.find_all(class_="mulu"):
    # print(mulu) #查看
    # 获取h2标签Tag
    h2 = mulu.find("h2")
    # print(h2) #查看

    if h2 != None:
        h2_title = h2.string #获取h2中的文本标题
        # print(h2_title) #查看

        list = []

        # 在每个mulu中获取章节名称及其链接标签a
        for a in mulu.find(class_="box").find_all("a"):
            href = a.get("href") # 从a标签中获取每个章节的连接
            # print(href) #查看
            box_title = a.get("title") # 从a标签中获取章节名称
            # print(box_title) # 查看
            list.append({"href" : href, "box_title" : box_title})
        content.append({"title" : h2_title, "content" : list})

with open("UserPython.json", "w", encoding="utf-8") as fp:
    json.dump(content, fp=fp, indent=4, ensure_ascii= False)

在这里插入图片描述

存储为CSV
CSV(称为逗号分隔值,有时也称为字符分隔值,因为分隔符也可以不是逗号),其文本以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据
CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其他字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。CSV文件示例如下

在这里插入图片描述

python使用CSV库来读写CSV文件。要将上面CSV文件的示例内容写成csvTest.csv文件,需要用到Writer对象,代码如下

import csv

headers = ["ID", "UserName", "Password", "Age", "Country"]

rows = [(1001,"userH","userH123", 19, "China"),
        (1002, "userN", "userN123", 20, "USA"),
        (1003, "userB", "userB123", 20, "UK")]

with open("csvTest.csv", "w") as f:
    f_csv = csv.writer(f)
    f_csv.writerow(headers)
    f_csv.writerows(rows)

里面的rows列表中的数据元组也可以是字典数据

import csv

headers = ["ID", "UserName", "Password", "Age", "Country"]

rows = [{"ID" : 1001, "UserName" : "userH", "Password" : "userH123", "Age" : 19, "Country" : "China"},
        {"ID" : 1002, "UserName" : "userN", "Password" : "userN123", "Age" : 20, "Country" : "USA"},
        {"ID" : 1003, "UserName" : "userB", "Password" : "userB123", "Age" : 20, "Country" : "UK"}]

with open("csvTest.csv", "w") as f:
    f_csv = csv.DictWriter(f, headers)
    f_csv.writeheader()
    f_csv.writerows(rows)

上面的代码中,row会是一个表列,因此,为了访问某个字段,你需要使用索引,如row[0]访问ID,row[3]访问Age。由于这种索引访问通常会引起混淆,因此可以考虑使用命名元组。示例如下

import csv

from collections import namedtuple

with open("csvTest.csv") as f:
    f_csv = csv.reader(f)
    headings = next(f_csv)
    Row = namedtuple("Row", headings)
    for r in f_csv:
        row = Row(*r)
        print(row.UserName, row.Password)
        # print(row)
#########################################       
userH userH123
userN userN123
userB userB123

它允许使用列名如row.UserName和row.Password代替下标访问。需要注意的是这个只有列名合法的Python标识符的时候才生效

除了使用命名组之外,另外一个解决办法就是读取到一个字典序列中,示例如下

import csv

with open("csvTest.csv") as f:
    f_csv = csv.DictReader(f)
    for row in f_csv:
        print(row["UserName"], row["Password"])
        # print(row.get("UserName"), row.get("Password"))

###########################
userH userH123
userN userN123
userB userB123

这样就可以使用列名去访问每一行的数据了。如row[“UserName”]或者row.get(“UserName”)

多媒体文件抽取

存储媒体文件主要有两种方式:只获取文件的URL链接,或者直接将媒体文件下载到本地。这里主要介绍urllib模块提供的urlretrieve()方法。urlretrieve()方法直接将远程数据下载到本地,方法原型如下

urlretrieve(url, filename=None, reporthook=None, data=None)

参数说明:
filename:参数filename指定了存储的本地路径(如果参数未指定,urllib会生成一个临时文件保存数据)
reporthook:参数reporthook是一个回调函数。当连接上服务器以及相应的数据块传输完毕时会触发该回调函数,我们可以利用这个回调函数来显示当前的下载进度
data:参数data指post到服务器的数据,该方法返回一个包含两个元素的(filename,headers)元组,filename表示保存到本地的路径,headers表示服务器的响应头

实践:提取天堂图片网的图片(http://www.ivsky.com/tupian/ziranfengguang/)
需求:提取当前网址中的图片链接,并将图片下载到指定目录

import urllib.request
from lxml import etree
import requests

def Schedule(blocknum, blocksize, totalsize):
    """
    :blocknum: 已经下载的数据块
    :blocksize: 数据块的大小
    :totalsize: 远程文件的大小
    """
    per = 100.0*blocknum*blocksize/totalsize
    if per > 100:
        per = 100
    print("当前下载进度:%d" % per)

user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
headers = {"User-Agent" : user_agent}

r = requests.get("http://www.ivsky.com/tupian/ziranfengguang/", headers = headers)

#使用lxml解析网页
html = etree.HTML(r.text)
img_urls = html.xpath(".//img/@src") #先找到所有的img
i = 0
for img_urls in img_urls:
    urllib.request.urlretrieve(img_urls,r'E:\Photo\img%s.jpg'%i, Schedule)
    i += 1

❤️ 先将当前网址的img标记中的src属性提取出来,交给urllib.request.urlretrieve函数去下载,自动回调Schedule函数,显示当前下载的进度

Email提醒

当爬虫在运行过程中遇到异常或者服务器遇到问题,可以通过Email及时向自己报告

发送邮件的协议是STMP,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮箱。Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件

将邮箱的SMTP开启后,我们来构造一个纯文本邮件:

from email.mime.text import MIMEText
msg = MIMETest(“Python爬虫运行异常,异常信息为遇到HTTP 403”, “plain”, “utf-8”)

构造MIMEText对象时需要3个参数:
邮件正文:如"Python爬虫运行异常,异常信息为遇到HTTP 403"
MIME的subtype,传入“plain”表示纯文本,最终的MIME就是“text/plain”
设置编码格式,UTF-8编码保证多语言兼容性

接着设置邮件的发件人、收件人和邮件主题等信息,并通过SMTP发送出去,代码如下

from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr
import smtplib

def _format_addr(s):
    name, addr =  parseaddr(s)
    return formataddr((Header(name, "utf-8").encode(),addr))

#发件人地址
from_addr = "[email protected]"
#邮箱密码
password="passwd"
#收件人地址
to_addr = "[email protected]"
#163网易邮箱服务器地址
smtp_server = "smtp.163.com"
#设置邮箱信息
msg = MIMEText("Python爬虫运行异常,异常信息为遇到HTTP 403", "plain", "utf-8")
msg["From"] = _format_addr("一号爬虫<%s>" % from_addr)
msg["To"] = _format_addr("管理员<%s>" % to_addr)
msg["Subject"] = Header("一号爬虫运行状态", "utf-8").encode()
#发送邮件
server = smtplib.SMTP(smtp_server, 25)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

有时候我们发送的可能不是纯文本,需要发送HTML邮件,将异常网页信息发送出去。在构造MIMEText对象时,把HTML字符串传进去,再把第二个参数由“plain”改为“html”就可以了,示例如下

msg = MIMEText('<html><body><h1>Hello python</h1>' + 
               '<p>异常网页<a href = "http://www.hhhh.com">...</p>' + 
               '</body></html>', "html", "utf-8")

猜你喜欢

转载自blog.csdn.net/UserPython/article/details/83065899