爬虫实战 - 爬取豆瓣top250 - 保存到SQLite中

  1 from bs4 import BeautifulSoup
  2 import urllib.request
  3 import urllib.parse
  4 import urllib.error
  5 import sqlite3
  6 import xlwt
  7 import re
  8 import os
  9 import time
 10 
 11 # 文件保存路径
 12 save_path = '豆瓣Top250.xls'
 13 dbpath = 'movieTop.db'
 14 
 15 # headers数据包
 16 head_package = {}
 17 head_package['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'
 18 # head_package['Cookie'] = 'll="118226"; bid=Qfmj76DkrD8; __utmv=30149280.20459; douban-profile-remind=1; gr_user_id=a58b8c23-ff09-4e14-91d7-599fd84a9682; _vwo_uuid_v2=D007BD5CC7E89E79A31FD5B37908B75EB|9cfed6ff673a9ef600e186fcb321944d; douban-fav-remind=1; viewed="3671502"; _vwo_uuid_v2=D007BD5CC7E89E79A31FD5B37908B75EB|9cfed6ff673a9ef600e186fcb321944d; ap_v=0,6.0; __utmc=30149280; __utmz=30149280.1618040804.22.19.utmcsr=douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmc=223695111; __utmz=223695111.1618040804.3.3.utmcsr=douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utma=30149280.756924018.1591256763.1618040804.1618043277.23; __utmb=30149280.0.10.1618043277; __utma=223695111.748862546.1609985591.1618040804.1618043277.4; __utmb=223695111.0.10.1618043277; _pk_ref.100001.4cf6=["","",1618043277,"https://www.douban.com/"]; _pk_ses.100001.4cf6=*; _pk_id.100001.4cf6=73809205b31654d3.1609985591.4.1618043308.1618041207.'
 19 
 20 # 正则表达式 - 规则 - 模板 字符串之前记得写 r
 21 # 链接
 22 re_link = re.compile(r'<a href="(.*?)">')  # 正则表达式中,()表示一个组-整体,该表达式是任意字符重复大于等于0次,并且有0或1个这样的字符串
 23 
 24 # 图片
 25 re_img = re.compile(r'<img .*src="(.*?)"', re.S)  # 忽略换行符
 26 
 27 # 片名
 28 re_title = re.compile(r'<span class="title">(.*)</span>')
 29 
 30 # 评分
 31 re_rating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>')
 32 
 33 # 评分人数
 34 re_people = re.compile(r'<span>(\d*人评价)</span>')  # 注意反斜杠 \d
 35 
 36 # 一句话
 37 re_quote = re.compile(r'<span class="inq">(.*)</span>')
 38 
 39 # 影片简介
 40 re_background = re.compile(r'<p class="">(.*?)</p>', re.S)  # 这里的 ?必须要加,原因见笔记
 41 
 42 
 43 # 打开链接
 44 def ask_url(para_url, para_head):
 45     request = urllib.request.Request(url=para_url, headers=para_head)
 46     para_html = ''  # response对象被 read后是二进制数据
 47     try:
 48         response = urllib.request.urlopen(request)
 49         para_html = response.read()
 50     except urllib.error as e:
 51         print(e.reason)
 52 
 53     return para_html
 54 
 55 
 56 # 保存数据到 excel中
 57 def save_data(para_datalist, para_savepath):
 58     my_workbook = xlwt.Workbook(encoding='utf-8', style_compression=0)  # workbook对象
 59     my_sheet1 = my_workbook.add_sheet('sheet1', cell_overwrite_ok=True)  # 工作表;cell_overwrite_ok表示是否以新内容覆盖单元格内原有的内容
 60     first_row = ('影片链接', '图片链接', '中文名', '外国名', '影片评分', '评分人数', '一句话评价', '影片简介')  # 写入Excel的第一行标题
 61     for i in range(0, 8):
 62         my_sheet1.write(0, i, first_row[i])
 63 
 64     # 写入爬取的250条内容
 65     count_movie = 0  # 计数:第几个电影
 66     for i in range(0, 250):
 67         # 每条中有8种信息
 68         for j in range(0, 8):
 69             my_sheet1.write(i + 1, j, para_datalist[count_movie + j])
 70 
 71         print('正在爬取第%d条电影' % (i + 1))
 72         count_movie += 8  # 切换到下一部电影
 73 
 74     # 保存Excel文件
 75     my_workbook.save(para_savepath)
 76     print('爬取完成!')
 77 
 78 
 79 # 获取数据
 80 def get_data(para_url, para_head):
 81     datalist = []
 82 
 83     # 顺序爬取豆瓣电影top250的10个网页
 84     for i in range(0, 10):
 85         # 每一页
 86         temp = 'start=' + str(i * 25)
 87         pre_url = para_url.replace('start=0', temp)
 88         temp_html = ask_url(pre_url, para_head).decode('utf-8')
 89         data = []
 90 
 91         # 爬取完成后对数据(temp_html)进行解析
 92         soup = BeautifulSoup(temp_html, 'html.parser')
 93         # find_all的新方法,标签名与类名同时查找(笔记中的 *kwargs)
 94         # 找到了所有的 item 标签,并将标签及其内容返回到了 temp
 95 
 96         # 每一条电影
 97         for temp in soup.find_all('div', class_='item'):
 98             # print(type(temp))  # 在 bs4中找到的标签等均是bs4的文件类型:bs4.element.Tag
 99             # 所以在下面的正则查找时要转换成 string 类型才可以
100             str_temp = str(temp)
101 
102             # 链接
103             temp_link = re.findall(re_link, str_temp)[0]  # 有可能在一个item中找到多个,我们只打印一个
104             data.append(temp_link)
105             # findall最后只返回组内元素 - 连接
106 
107             # 图片
108             temp_img = re.findall(re_img, str_temp)[0]
109             data.append(temp_img)
110 
111             # 名称 - 注意其中包含中文名与外国名两个元素
112             temp_title = re.findall(re_title, str_temp)
113             if len(temp_title) == 2:
114                 data.append(temp_title[0])  # 中文名
115                 data.append(temp_title[1].replace('/', '').strip())  # 英文名, 并且把英文名前的斜杠消除
116             else:
117                 data.append(temp_title[0])
118                 data.append(' ')  # 若没有英文名,则必须置为空,保证顺序
119 
120             # 评分
121             temp_score = re.findall(re_rating, str_temp)
122             data.append(temp_score[0])
123 
124             # 评分人数
125             temp_people = re.findall(re_people, str_temp)[0]
126             data.append(temp_people)
127 
128             # 一句话
129             temp_quote = re.findall(re_quote, str_temp)
130             if temp_quote:
131                 data.append(temp_quote[0].replace('', ''))
132             else:
133                 data.append(' ')
134 
135             # 简介
136             temp_background = re.findall(re_background, str_temp)[0]
137             # 去掉<br/>
138             temp_background = re.sub(r'<br(\s+)?/>(\s+)?', '', temp_background)  # \s匹配空格、空白、tab
139             # 去掉 /
140             temp_background = re.sub(r'/', ' ', temp_background)
141             # 去掉两个及以上的不间断空白符 \xa0或空格
142             temp_background = re.sub('(\xa0| )+', ' ', temp_background)
143             data.append(temp_background.strip())  # strip函数去掉开头结尾的换行符和空格
144 
145             # 将子列表 data加入到总列表 datalist中
146             datalist.append(data)
147             # 临时列表 data要置为空!
148             data = []
149     print(datalist)  # 测试 datalist
150     return datalist
151 
152 
153 # 保存数据到 SQLite数据库中
154 def save_database(para_datalist, para_dbpath):
155     conn = sqlite3.connect(para_dbpath)
156     c = conn.cursor()
157 
158     for data in para_datalist:
159         for index in range(0, len(data)):
160             if index == 4:
161                 continue
162             data[index] = '"' + data[index] + '"'  # 加上引号,为后面插入数据做准备
163 
164         sql = '''
165         insert into Top_movie250(
166         movie_link, img_link, chinese_name, other_name, rating, rating_people, quote, background
167         ) values (%s)
168         ''' % ','.join(data)
169 
170         c.execute(sql)
171         conn.commit()
172 
173     conn.commit()
174     conn.close()
175 
176 
177 # 创建数据库中的表格
178 def create_table(para_dbpath):
179     # 检测数据库文件是否存在
180     if para_dbpath in os.listdir():
181         return print('该数据库文件已存在!')
182 
183     conn = sqlite3.connect(para_dbpath)  # 连接
184     c = conn.cursor()  # 游标
185 
186     # sql语句
187     sql = '''
188     create table Top_movie250(
189     id integer primary key autoincrement not null,
190     movie_link text,
191     img_link text,
192     chinese_name text,
193     other_name text,
194     rating numeric,
195     rating_people text,
196     quote text,
197     background text
198     )
199     '''
200 
201     c.execute(sql)
202 
203     conn.commit()  # 提交
204     conn.close()  # 关闭
205 
206 
207 # 代理函数
208 def proxy(para_url):
209     property_support = urllib.request.ProxyHandler({'http': '125.73.220.18:49128'})
210     opener = urllib.request.build_opener(property_support)
211     opener.addheaders = [('User-Agent','Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/89.0.4389.82Safari/537.36')]
212     urllib.request.install_opener(opener)
213     html = ask_url(para_url, head_package)
214     return html
215 
216 # 爬取数据并保存在 excel中
217 natural_url = 'http://movie.douban.com/top250?start=0&filter='
218 douban_dataList = get_data(natural_url, head_package)
219 # save_data(douban_dataList, save_path)
220 
221 
222 # 创建 db文件并创建数据库中的表格
223 create_table(dbpath)
224 
225 # 存数据到数据库中
226 save_database(douban_dataList, dbpath)

 结果:

 

 

存在的bug:

①多次运行会多次插入250条数据,不会重新覆盖

②要重新覆盖必须直接删除movieTop.db数据库文件,不能直接删表。将代码中第179-181行删掉可以实现:只删除表格不删db文件即可重新执行

 

posted @ 2021-04-17 16:22  vosoland  阅读(139)  评论(0)    收藏  举报