(本博客只为技术分学习,无其他用途)
1.准备
涉及的第三方库如下:
2.网页分析
2.1 检验网页
1. 运行浏览器,打开网页,按快捷键F12打开开发者工具,F5刷新页面
2. 在右侧点击Network,打开browser?sort=rank&page=1 文件,可以看到各种信息,查看表头
3. 获取Cooki 和User-Agnet,准备伪装浏览器
cooki = { 'Cooki':'chii_sid=9d93vQ; chii_sec_id=A56jP7ywgsTIyHVgRdkNaKLWSr78u1GyIrwvbA; chii_theme=light; __utma=1.22392930.1681741390.1681741390.1681741390.1; __utmc=1; __utmz=1.1681741390.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmt=1; _ga=GA1.1.1822559156.1681741390; chii_cookietime=0; chii_auth=U8%2Bgbe6%2BiJDdyyVqQNkHI%2FuCHsKf8GWIQb8JdwidsW7q0xwNW5HYfQLMVqJYAGl9SZsfA5o4RYtgWU0SpRtCYczS5gmQSyu%2FitIq; __utmb=1.11.10.1681741390; _ga_1109JLGMHN=GS1.1.1681741390.1.1.1681741903.0.0.0' } head = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.1.4031 SLBChan/25 ', 'Connection': 'close'} # 当前正在使用的tcp链接在当天请求处理完毕后会被断掉
4.使用Requests发送GET请求
定义function:
# 获取网址请求 def get_url(url): response = requests.get(url=url, timeout=600, cookies=cooki, headers=head, verify=False) # 用verify关键字参数,在请求的时候不验证网站的ca证书 data = response.text.encode('ISO-8859-1').decode( 'utf-8') # Python 终端的编码方式是 UTF-8 ,而 HTML 编码方式并不是 UTF-8 (可通过 response.encoding 查询页面编码格式),为了避免在匹配标题时中文产生了乱码 html = etree.HTML(data) # 解析字符串格式的HTML文档对象,将传进去的字符串转变成_Element对象 return html
5.xpath语法
推荐这个博主的讲解
(120条消息) 史上最全的xpath定位方法 全在这了!_七月的小尾巴的博客-CSDN博客
2.2 网址页面分析
第一页:
第二页:
由此可知页面的切换与 page 有关,第N页的网址只要修改为 https://--------------------page = N
由此获取100条网址的列表:
urls = [('https://bangumi.tv/anime/browser?sort=rank&page=' + f'{x}') for x in range(1, 101)] # 网址列表 2.3 xpath 元素分析 每个番剧的元素设计模式都是相似的,找出模式再利用xpath分析即可获取 1.分析id
定位标签,xpath语句:
id_content = html.xpath('//div[@class = "section"]/ul/li/@id')
定义function:
# 匹配id def get_id(html): id_content = html.xpath('//div[@class = "section"]/ul/li/@id') for i in id_content: if i: # 判断是否为空 id.append(i) else: id.append('None')
2. 链接分析
定位标签,xpath语句:
link_content = html.xpath('//div[@class = "section"]/ul/li/a/@href')
获取的网址还是残缺的,要加上前缀
定义function:
# 匹配链接 def get_link(html): link_content = html.xpath('//div[@class = "section"]/ul/li/a/@href') for lk in link_content: link.append('https://bangumi.tv/' + lk) # 组合链接网址并存储
3.图片分析
定位标签,xpath语句:
img_content = html.xpath('//span[@class ="image"]/img/@src')
图片有两种类型jpg和png,用split分割获取图片类型,地址还是残缺的,根据图片类型加上不同的前缀
定义function:
# 获取图片地址 def get_img_link(html): img_content = html.xpath('//span[@class ="image"]/img/@src') for i in img_content: if i: tile = str(i).split('.')[-1] # 获取图片类型 if tile == 'jpg': # 如果为jpg图片 img.append('https:' + str(i)) # 组合图片网址并存储 if tile == 'png': # 如果为png图片 img.append('https://bangumi.tv' + str(i)) # 组合图片网址并存储
4.排名分析
定位标签,xpath语句:
rank_content = html.xpath('//span[@ class ="rank"]/text()')
有时候会返回空字符串,所以要判断处理以下再存入列表
定义function:
# 匹配排名 def get_rank(html): rank_content = html.xpath('//span[@ class ="rank"]/text()') for r in rank_content: if r: # 判断是否为空 rank.append(r) else: rank.append('None')
5.番名分析
定位标签,xpath语句:
name_content = html.xpath('//h3/a[@class = "l"]/text()')
有时候会返回空字符串,所以要判断处理以下再存入列表
定义function:
# 匹配番名 def get_name(html): name_content = html.xpath('//h3/a[@class = "l"]/text()') for n in name_content: if n: # 判断是否为空 name.append(n) else: name.append('None')
6.信息分析(包括集数,日期,创作人员)
定位标签,xpath语句:
information_content = html.xpath('//p[@ class ="info tip"]/text()')
返回的信息以split以‘/’分割两次,获取三段数据,分别为集数、日期、制作人员,判断是否空分再别存入对应的列表
定义function:
# 匹配信息 def get_infor(html): information_content = html.xpath('//p[@ class ="info tip"]/text()') for i in information_content: i = str(i).split('/', 2) # 将数据类型转换为字符串且以‘/’分割两次 # 某些数据有残缺,要判断元素个数 if len(i) == 1: episode.append(i[0].strip()) # 提取集数即列表的第一个元素,去除空格 date.append('未知') creator.append('未知') if len(i) == 2: episode.append(i[0].strip()) # 提取集数即列表的第一个元素,去除空格 date.append(i[1].strip()) # 不为空则提取日期即列表的第二个元素,去除空格 creator.append('未知') if len(i) == 3: episode.append(i[0].strip()) # 提取集数即列表的第一个元素,去除空格 date.append(i[1].strip()) # 提取日期即列表的第二个元素,去除空格 creator.append(i[-1].strip()) # 提取创作人员即列表的最后一个元素,去除空格
7. 评分分析
定位标签,xpath语句:
rating_content = html.xpath('//small[@ class ="fade"]/text()')
定义function:
# 匹配评分 def get_rating(html): rating_content = html.xpath('//small[@ class ="fade"]/text()') for r in rating_content: if r: # 判断是否为空 rating.append(r) else: rating.append("None")
8. 评价人数分析
定位标签,xpath语句:
person_content = html.xpath('//span[@class ="tip_j"]/text()')
定义function:
# 匹配评价人数 def get_person(html): person_content = html.xpath('//span[@class ="tip_j"]/text()') for p in person_content: if p: # 判断是否为空 person.append(p) else: person.append('None')
9.下载单个图片
先判断图片类型再以不同方式命名图片,将图片以二进制模式写入到到目标文件目录下
定义function:
# 下载单个图片 def download_img(address): path = os.path.abspath('Image') # 获取文件夹当前绝对路径 t = address.split('.')[-1] # 获取图片类型 if t == 'jpg': picture_name = 'item_' + address.split('/')[-1].split('_')[0] # 获取id数字 file_name = path + '\{name}.jpg'.format(name =picture_name) # 设置文件名 t = random.uniform(2, 8) # 定义随机暂停时间 img_response = requests.get(address) time.sleep(t) with open(file_name, 'wb') as f: f.write(img_response.content) # 以二进制格式写入图片 print('图片下载完成') if t == 'png': file_name = path + r'\None.png' # 设置文件名 t = random.uniform(2, 8) # 定义随机暂停时间 img_response = requests.get(address) time.sleep(t) if not os.path.exists(file_name): with open(file_name, 'wb') as f: f.write(img_response.content) # 以二进制格式写入图片 print('图片下载完成')
3.线程池
推荐这位博主的讲解
python并发编程之多线程和线程池 - 掘金 (juejin.cn)
Exectuor 主要方法:
- submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
推荐这位博主的讲解:
python线程池 ThreadPoolExecutor 使用详解_生活不允许普通人内向的博客-CSDN博客
定义function:
# 线程池 def thread_pool(): thread_results = [] urls = [('https://bangumi.tv/anime/browser?sort=rank&page=' + f'{x}') for x in range(1, 101)] # 网址列表 with ThreadPoolExecutor() as pool: # 创建线程池 futures = [pool.submit(get_url, url) for url in urls] # 将get_url函数递交给线程池,传入参数为url for future in as_completed(futures): # 按线程完成顺序返回,先完成先返回 thread_results.append(future.result()) # 获取任务返回值 return thread_results
4. 执行函数
# 执行函数 if __name__ == "__main__": spider_start = time.time() # 计时爬虫开始 results = thread_pool() # 接收函数返回值 for result in results: # 调用函数 get_id(result) get_link(result) get_rank(result) get_name(result) get_img_link(result) get_infor(result) get_rating(result) get_person(result) spider_end = time.time() #计时爬虫结束 print(f'爬取数据消耗了{spider_end-spider_start}') print(id, '\n', len(id)) # 打印爬取结果 print(link, '\n', len(link)) print(img, '\n', len(img)) print(rank, '\n', len(rank)) print(name, '\n', len(name)) print(episode, '\n', len(episode)) print(date, '\n', len(date)) print(creator, '\n', len(creator)) print(rating, '\n', len(rating)) print(person, '\n', len(person))
5. 数据库操作
1. 创建表
CREATE TABLE Anime ( Id VARCHAR (100 ) NOT NULL PRIMARY KEY, Link VARCHAR ( 100 ), Name_anime VARCHAR ( 100 ), Rank_anime CHAR ( 50 ) , Episode VARCHAR ( 100 ) , Date_time VARCHAR ( 300 ), Creator VARCHAR ( 200 ) , Rating FLOAT ( 10 ) , Person_num CHAR ( 40 ) , Mark CHAR ( 10 ) );
2.执行sql语句,插入数据
# 创建数据库连接 sql_start = time.time() # 计时数据插入开始 db = pymysql.connect(host='localhost', user='root', password='318427', database='animeplan') # 使用cursor()方法获取操作游标 cursor = db.cursor() sql = """ insert into anime(Id,Link,Name_anime,Rank_anime,Episode,Date_time,Creator,Rating,Person_num) values (%s,%s,%s,%s,%s,%s,%s,%s,%s) """ # 循环插入数据 for num in range(len(id)): try: cursor.execute(sql, ( id[num], link[num], name[num], rank[num], episode[num], date[num], creator[num], rating[num], person[num])) db.commit() print('数据插入成功') except Exception as e: print(e) print('数据插入错误') db.close() sql_end = time.time() # 计时数据插入结束 print(f'插入数据消耗了{sql_end-sql_start}')
6. 启用线程池爬取图片
# 下载图片 try: img_start = time.time() if os.path.exists('Image'): # 判断路径存在 print('Image文件夹已经创建,开始下载图片') with ThreadPoolExecutor() as image_poll: # 创建线程池 image_futures = [image_poll.submit(download_img, address) for address in img] # 递交download_img函数,传入address图片地址 for im_future in image_futures: print(f'{im_future.done()}') # 返回任务执行状态,判断该任务是否结束 else: print('建立Image文件夹,开始下载图片') os.mkdir('Image') # 创建文件夹来存储图片文件 with ThreadPoolExecutor() as image_poll: image_futures = [image_poll.submit(download_img, address) for address in img] for im_future in image_futures: print(f'{im_future.done()}') img_end = time.time() print(f'花费了{img_end - img_start}') except Exception as e: print(e, '\n', '图片下载失败')
警告:
本文只是用于技术学习,绝无其他用途!
读者不可用于其他用途,后果自负!
标签:pathon,content,img,xpath,爬取,html,print,番剧,append From: https://www.cnblogs.com/Mokirito/p/17334497.html