第三个爬虫实战代码(使用xpath表达式)

1. 将数据存储到CSV文件中

前面爬取的数据要么打印到屏幕上,要么存储到TXT文件中,这些个是不利于数据的存储。

所以我推荐一个存储格式:CSV格式。CSV格式是存储表格数据的常用文件格式,Excel和很多应用都支持CSV格式,因为他很简洁。

例如:

import csv
fp = open('D:/python爬虫/test.csv','w+')    # 以写入模式打开文件,如果文件不存在,则创建一个新文件
writer = csv.writer(fp)    # 创建一个用于写入CSV文件的写入器对象语句
writer.writerow(('id','name'))      #writerow()方法将一行数据写入csv文件
writer.writerow(('1','xioaming'))
writer.writerow(('2','张三'))
writer.writerow(('4','李四'))
fp.close()

2.爬取豆瓣网图书TOP250的10页信息思路分析

2.1 分析豆瓣网图书TOP250的10页url

目标网站:
https://book.douban.com/top250
可看到:
书名,坐着名字,出版社,出书年,价格
往下翻可以看到这是翻页的
第一页:https://book.douban.com/top250
第二页:https://book.douban.com/top250?start=25
第三页:https://book.douban.com/top250?start=50
第四页:https://book.douban.com/top250?start=75
第五页:https://book.douban.com/top250?start=100
...
因为数据太多,我们就只获取10页就ok了

所以试试第一页:https://book.douban.com/top250?start=0
是可以的

所以总结出的规律:

方法一:

start的值步长为25,所以我们可以创建一个列表用来装start值,且只需要前十页。
list=[0  25   50   75   100  125  150  175  200 225]
urls = ['https://book.douban.com/top250?start={}'.format(str(i)) for i in list]

方法二:

start的值步长为25,我们可以使用range()函数,range(0,226,25)可以解释为:生成一个0到225(不包括226),步长为25的整数序列。
urls = ['https://book.douban.com/top250?start={}'.format(str(i)) for i in range(0, 226, 25)]

2.2 分析我们要爬取的内容

需要爬取的信信息有:书名,书本的URL链接,作者,出版社和出版时间,书本价格,评分和评价


分析内容应该是“先抓大后抓小,寻找循环点”

如图:

可以看到书的信息都在我所框出来标签中,我们可以此为循环点。

从张图可以看出,我们具体的信息都在tr标签下。

第一步:先抓大,定位到每个段子信息,这就是循环点

在tr标签下的class属性,右击后点击“检查”,选择Copy Xpath:
//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr
//*[@id="content"]/div/div[1]/div/table[2]/tbody/tr
//*[@id="content"]/div/div[1]/div/table[3]/tbody/tr

...

可以看出这个就是循环点

infos = html.xpath('//*[@id="content"]')  # 取大标签,以此循环

我们就可以写出循环点的循环语句:

for info in infos:

        ...

书名:bookname
通过Chrome浏览器进行检查,定位到bookname:

//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div[1]/a
//*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div[1]/a

因为第一部分为循环点,将其删除得到:
tbody/tr/td[2]/div[1]/a
tbody/tr/td[2]/div[1]/a

(‘td/div/a/@title’)[0]是什么意思
整个表达式的含义是选择第一个匹配的a元素的@title属性的值:

书本的URL链接:bookurl
https://book.douban.com/subject/1007305/
根据上一个我们写的xpath表达式可以是
选择第一个匹配的a元素的@href属性的值
(‘td/div/a/@href’)[0]

作者:author
<p class="pl">[清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元</p>
可以看到p标签下只有一个属性class所以这里不需要写属性即可定位到

td/p/text()   获取怕标签下的文本
但是所得到的是这样一长串的,并不是我们要的作者名字
[清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元
我们["[清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元"]
所以可以进行切片获取
.split(“/”)方法就可以得到["[清] 曹雪芹 著(0) ","人民文学出版社(1)","1996-12(2)","59.70元(3)"]
author[0]
press[1]
presstime[2]
book_price[3]
出版社:press
出版时间:presstime
书本价格book_price
<p class="pl">[清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元</p>

评分:grade
//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div[2]/span[2]
td/div/span[2]/text()


评价:comments
//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/p[2]/span
td/p/span/text()
评论这个有的有,有的没有
所以可以写一个判断语句
comment=comments[0] if len(comments) !=0 else "空"

因为xpath() 是一种在 XML 和 HTML 文档中查找元素的方法。
它返回的是符合指定路径的元素列表。

      

将这些理清楚后就可以编写代码了:

# 1.导包
import requests
from lxml import etree
import csv

# 2.伪装
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'}


# 3.函数get_info
def get_infos(url):
    # 函数部分可以分为:a.使用get方法发送get请求,获取请求信息
    res = requests.get(url, headers=headers)
    # HTML()函数: 是python中lxml库的一个函数,
    # 用于解析HTML文档并生成一个Element对象,
    # 这个对象可以使用Xpath表达式进行查询.
    html = etree.HTML(res.text)
    # 		b.信息部分:把我们刚刚写的xpath表达式是放进去
    #    先抓大,定位到每个段子信息,这就是循环点

    infos = html.xpath('//tr[@class="item"]')
    all_infos = []  # 存储所有信息的列表
    for info in infos:
        bookname = info.xpath('td/div/a/@title')[0] if info.xpath('td/div/a/@title') else ""
        bookurl = info.xpath('td/div/a/@href')[0] if info.xpath('td/div/a/@href') else ""
        bookinfo = info.xpath('td/p/text() ')[0].strip() if info.xpath('td/p/text() ') else ""
        author = bookinfo.split('/')[0] if '/' in bookinfo else ""
        press = bookinfo.split('/')[1] if len(bookinfo.split('/')) > 1 else ""
        presstime = bookinfo.split('/')[2] if len(bookinfo.split('/')) > 2 else ""
        book_price = bookinfo.split('/')[3] if len(bookinfo.split('/')) > 3 else ""
        grade = info.xpath('td/div/span[2]/text()')[0] if info.xpath('td/div/span[2]/text()') else ""
        comments = info.xpath('td/p/span/text()') if info.xpath('td/p/span/text()') else ""
        comment = comments[0] if comments else "空"
        all_info = (bookname, bookurl, author, press, presstime, book_price, grade, comment)
        all_infos.append(all_info)  # 将每个信息添加到all_infos列表中
    return all_infos  # 返回包含所有信息的列表

# 4.主程序部分
if __name__ == '__main__':
    # a url部分
    urls = ['https://book.douban.com/top250?start={}'.format(str(i)) for i in range(0, 226, 25)]
    # b 创建打开,写写入csv,write+文件部分
    f = open('D:/python爬虫/douban1.csv', 'wt',newline='',encoding='UTF-8')
    writer = csv.writer(f)  # 创建一个用于写入CSV文件的写入器对象语句
    writer.writerow(('bookname', 'bookurl', 'author', 'press', 'presstime', 'book_price', 'grade', 'comment'))
    for url in urls:
        for i in get_infos(url):
            writer.writerow(i)  # writerow()方法将一行数据写入csv文件
    # c  关闭文件
    f.close()

代码解析:

我在编写代码的时候也是遇到了问题:

1.

一开始按照我的思路循环点的xpath路径应该是:infos = html.xpath('//*[@id="content"]')

而运行之后确是空列表,出现这中空列表情况后,我搜了一下可能有两种情况:

A.网络连接不正常

B.xpath路径可能不正确

我确定我的网络连接正常后,就只有可能是我的xpath路径不正确了。

所以修改后的xpath路径:infos = html.xpath('//tr[@class="item"]')

2.

comments = info.xpath('td/p/span/text()')[0] if info.xpath('td/p/span/text()') else ""
comment = comments[0] if comments else 

这是我原来写的代码,运行之后列表的comment只有一个子,经过我的分析是出在了comments中的索引[0]上,因为如果取索引[0]我们得到的内容是就是我们需要的内容了,在经过comment = comments[0] if comments else一下就就只取出来一个字。

3.

writer.writerow('bookname', 'bookurl', 'author', 'press', 'presstime', 'book_price', 'grade', 'comment')
return bookname, bookurl, author, press, presstime, book_price, grade, comment)

没有把信息加入到元组,直接返回信息。输出时,一行信息都挤在一个格子里。

在python中括号用于创建元组。如果我直接写入'bookname', 'bookurl', 'author', 'press', 'presstime', 'book_price', 'grade', 'comment',那么这将是一个包含8个字符串元素的列表,而不是元组。在使用writerow()方法时,你需要将这些字符串元素放在一个元组中,以便它们作为单个参数传递给writerow()。实际上,在pthon中,元组和列表都可以在一个函数中作为参数传递。

所以正确的是,将信息整合到元组里:

all_info = (bookname, bookurl, author, press, presstime, book_price, grade, comment)
all_infos.append(all_info)  # 将每个信息添加到all_infos列表中
writer.writerow(('bookname', 'bookurl', 'author', 'press', 'presstime', 'book_price', 'grade', 'comment'))

运行后会在你设置的目录里出现该文件,打开会看见乱码了,可以用记事本打开,另存格式为UTF-8,如果不行还可以试试带有BOM的UTF-8:

总结:xpath路径可以很有效的批量爬取一个网页的内容,只需要掌握“先抓大后抓小,寻找循环点这一句话”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值