爬虫实战 - 爬取豆瓣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文件即可重新执行

浙公网安备 33010602011771号