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路径可以很有效的批量爬取一个网页的内容,只需要掌握“先抓大后抓小,寻找循环点这一句话”。