Python爬蟲入門:爬取pixiv


終於想開始爬自己想爬的網站了。於是就試着爬P站試試手。

我爬的圖的目標網址是: http://www.pixiv.net/search.php?word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98,目標是將每一頁的圖片都爬下來。

一開始以為不用登陸,就直接去爬圖片了。

后來發現是需要登錄的,但是不會只好去學模擬登陸。

這里是登陸網站 https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index 的headers,

然后還要去獲取我們登陸時候需要的data。點住上面的presevelog,找到登陸的網址,點開查看Form Data就可以知道我們post的時候的data需要什么了。這里可以看到有個postkey,多試幾次可以發現這個是變化的,即我們要去捕獲它,而不能直接輸入。

 

於是退回到登陸界面,F12查看源碼,發現有一個postkey,那么我們就可以寫一個東西去捕獲它,然后把它放到我們post的data里面。

這里給出登陸界面需要的代碼:

 1     def __init__(self):
 2         self.base_url = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index'
 3         self.login_url = 'https://accounts.pixiv.net/api/login?lang=zh'
 4         self.target_url = 'http://www.pixiv.net/search.php?' \
 5                           'word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98&order=date_d&p='
 6         self.main_url = 'http://www.pixiv.net'
 7         # headers只要這兩個就可以了,之前加了太多其他的反而爬不上
 8         self.headers = {
 9             'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
10             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) '
11                           'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
12         }
13         self.pixiv_id = 'userid'
14         self.password = '*****'
15         self.post_key = []
16         self.return_to = 'http://www.pixiv.net/'
17         self.load_path = 'D:\psdcode\Python\pixiv_pic'
18         self.ip_list = []
19 
20     def login(self):
21         post_key_html = se.get(self.base_url, headers=self.headers).text
22         post_key_soup = BeautifulSoup(post_key_html, 'lxml')
23         self.post_key = post_key_soup.find('input')['value']
24         # 上面是去捕獲postkey
25         data = {
26             'pixiv_id': self.pixiv_id,
27             'password': self.password,
28             'return_to': self.return_to,
29             'post_key': self.post_key
30         }
31         se.post(self.login_url, data=data, headers=self.headers)

愉快地解決完登陸問題之后,就可以開始爬圖片啦。

進入target_url:上面的目標網址。

點擊目標的位置

 

點開ul這個標簽,發現圖片全部都是在<li class="image-item">這里面的,因為我們要爬大一點的圖(爬個小圖有什么用啊!),所以還要進入一層第一個鏈接的網址去獲取大圖,我們可以發現我們只要在main_url((http://www.pixiv.net)),再加上第一個href,就可以跑到圖片所在的網址了,於是我們先跳轉到圖片網址看看怎么提取圖片。

發現圖片就躺在這里了,而且連標題都有,直接方便了我們存圖的名字了。於是我們就可以直接去提取圖片了。

注意我們在請求獲取圖片的時候要加一個referer,否則會403的。referer的找法就和上面一樣。

 1     def get_img(self, html, page_num):
 2         li_soup = BeautifulSoup(html, 'lxml')  # 傳入第page_num頁的html
 3         li_list = li_soup.find_all('li', attrs={'class', 'image-item'})   # 找到li所在位置
 4         # print('get_list succeed')
 5         # print(li_list)
 6         for li in li_list:
 7             href = li.find('a')['href']  # 直接提取第一個href
 8             # print('get_href succeed')
 9             # print(href)
10             jump_to_url = self.main_url + href  # 跳轉到目標的url
11             # print('get_jump_to_url succeed')
12             jump_to_html = self.get_html(jump_to_url, 3).text  # 獲取圖片的html
13             # print('get_jump_to_html succeed')
14 
15             img_soup = BeautifulSoup(jump_to_html, 'lxml')
16             img_info = img_soup.find('div', attrs={'class', 'works_display'})\
17                 .find('div', attrs={'class', '_layout-thumbnail ui-modal-trigger'})
18             # 找到目標位置的信息
19             if img_info is None:  # 有些找不到url,如果不continue會報錯
20                 continue
21             self.download_img(img_info, jump_to_url, page_num)  # 去下載這個圖片
22 
23     def download_img(self, img_info, href, page_num):
24         title = img_info.find('img')['alt']  # 提取標題
25         src = img_info.find('img')['src']  # 提取圖片位置
26         src_headers = self.headers
27         src_headers['Referer'] = href  # 增加一個referer,否則會403,referer就像上面登陸一樣找
28         try:
29             html = requests.get(src, headers=src_headers)
30             img = html.content
31         except:  # 有時候會發生錯誤導致不能獲取圖片.直接跳過這張圖吧
32             print('獲取該圖片失敗')
33             return False

接下來輪到下載圖片了。這個之前還不怎么會,臨時學了一下。

首先是創建文件夾,我這里是每一頁就開一個文件夾。

 1     def mkdir(self, path):
 2         path = path.strip()
 3         is_exist = os.path.exists(os.path.join(self.load_path, path))
 4         if not is_exist:
 5             print('創建一個名字為 ' + path + ' 的文件夾')
 6             os.makedirs(os.path.join(self.load_path, path))
 7             os.chdir(os.path.join(self.load_path, path))
 8             return True
 9         else:
10             print('名字為 ' + path + ' 的文件夾已經存在')
11             os.chdir(os.path.join(self.load_path, path))
12             return False
 1    def download_img(self, img_info, href, page_num):
 2         title = img_info.find('img')['alt']  # 提取標題
 3         src = img_info.find('img')['src']  # 提取圖片位置
 4         src_headers = self.headers
 5         src_headers['Referer'] = href  # 增加一個referer,否則會403,referer就像上面登陸一樣找
 6         try:
 7             html = requests.get(src, headers=src_headers)
 8             img = html.content
 9         except:  # 有時候會發生錯誤導致不能獲取圖片.直接跳過這張圖吧
10             print('獲取該圖片失敗')
11             return False
12 
13         title = title.replace('?', '_').replace('/', '_').replace('\\', '_').replace('*', '_').replace('|', '_')\
14             .replace('>', '_').replace('<', '_').replace(':', '_').replace('"', '_').strip()
15         # 去掉那些不能在文件名里面的.記得加上strip()去掉換行
16 
17         if os.path.exists(os.path.join(self.load_path, str(page_num), title + '.jpg')):
18             for i in range(1, 100):
19                 if not os.path.exists(os.path.join(self.load_path, str(page_num), title + str(i) + '.jpg')):
20                     title = title + str(i)
21                     break
22         # 如果重名了,就加上一個數字
23         print('正在保存名字為: ' + title + ' 的圖片')
24         with open(title + '.jpg', 'ab') as f:
25             f.write(img)
26         print('保存該圖片完畢')

這樣我們的大體工作就做完了。剩下的是寫一個work函數讓它開始跑。

 1     def work(self):
 2         self.login()
 3         for page_num in range(1, 51):  # 太多頁了,只跑50頁
 4             path = str(page_num)  # 每一頁就開一個文件夾
 5             self.mkdir(path)  # 創建文件夾
 6             # print(self.target_url + str(page_num))
 7             now_html = self.get_html(self.target_url + str(page_num), 3)  # 獲取頁碼
 8             self.get_img(now_html.text, page_num)  # 獲取圖片
 9             print('第 {page} 頁保存完畢'.format(page=page_num))
10             time.sleep(2)  # 防止太快被反

啟動!

大概跑了10頁之后,會彈出一大堆信息什么requests不行怎么的。問了下別人應該是被反爬了。

於是去搜了一下資料,http://cuiqingcai.com/3256.html,照着他那樣寫了使用代理的東西。(基本所有東西都在這學的)。

於是第一個小爬蟲就好了。不過代理的東西還沒怎么懂,到時候看看,50頁爬了兩個多鍾。

對了。可能網站的源代碼會有改動的。因為我吃完飯后用吃飯前的代碼繼續工作的時候出錯了,然后要仔細觀察重新干。

  1 # -*- coding:utf-8 -*-
  2 import requests
  3 from bs4 import BeautifulSoup
  4 import os
  5 import time
  6 import re
  7 import random
  8 
  9 se = requests.session()
 10 
 11 
 12 class Pixiv():
 13 
 14     def __init__(self):
 15         self.base_url = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index'
 16         self.login_url = 'https://accounts.pixiv.net/api/login?lang=zh'
 17         self.target_url = 'http://www.pixiv.net/search.php?' \
 18                           'word=%E5%9B%9B%E6%9C%88%E3%81%AF%E5%90%9B%E3%81%AE%E5%98%98&order=date_d&p='
 19         self.main_url = 'http://www.pixiv.net'
 20         # headers只要這兩個就可以了,之前加了太多其他的反而爬不上
 21         self.headers = {
 22             'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
 23             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) '
 24                           'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
 25         }
 26         self.pixiv_id = 'userid'
 27         self.password = '*****'
 28         self.post_key = []
 29         self.return_to = 'http://www.pixiv.net/'
 30         self.load_path = 'D:\psdcode\Python\pixiv_pic'
 31         self.ip_list = []
 32 
 33     def login(self):
 34         post_key_html = se.get(self.base_url, headers=self.headers).text
 35         post_key_soup = BeautifulSoup(post_key_html, 'lxml')
 36         self.post_key = post_key_soup.find('input')['value']
 37         # 上面是去捕獲postkey
 38         data = {
 39             'pixiv_id': self.pixiv_id,
 40             'password': self.password,
 41             'return_to': self.return_to,
 42             'post_key': self.post_key
 43         }
 44         se.post(self.login_url, data=data, headers=self.headers)
 45 
 46     def get_proxy(self):
 47         html = requests.get('http://haoip.cc/tiqu.htm')
 48         ip_list_temp = re.findall(r'r/>(.*?)<b', html.text, re.S)
 49         for ip in ip_list_temp:
 50             i = re.sub('\n', '', ip)
 51             self.ip_list.append(i.strip())
 52             print(i.strip())
 53 
 54     ''' 會被反爬,改成使用代理
 55         def get_tml(self, url):
 56             response = se.get(url, headers=self.headers)
 57             return response
 58     '''
 59     def get_html(self, url, timeout, proxy=None, num_entries=5):
 60         if proxy is None:
 61             try:
 62                 return se.get(url, headers=self.headers, timeout=timeout)
 63             except:
 64                 if num_entries > 0:
 65                     print('獲取網頁出錯,5秒后將會重新獲取倒數第', num_entries, '')
 66                     time.sleep(5)
 67                     return self.get_html(url, timeout, num_entries = num_entries - 1)
 68                 else:
 69                     print('開始使用代理')
 70                     time.sleep(5)
 71                     ip = ''.join(str(random.choice(self.ip_list))).strip()
 72                     now_proxy = {'http': ip}
 73                     return self.get_html(url, timeout, proxy = now_proxy)
 74         else:
 75             try:
 76                 return se.get(url, headers=self.headers, proxies=proxy, timeout=timeout)
 77             except:
 78                 if num_entries > 0:
 79                     print('正在更換代理,5秒后將會重新獲取第', num_entries, '')
 80                     time.sleep(5)
 81                     ip = ''.join(str(random.choice(self.ip_list))).strip()
 82                     now_proxy = {'http': ip}
 83                     return self.get_html(url, timeout, proxy = now_proxy, num_entries = num_entries - 1)
 84                 else:
 85                     print('使用代理失敗,取消使用代理')
 86                     return self.get_html(url, timeout)
 87 
 88     def get_img(self, html, page_num):
 89         li_soup = BeautifulSoup(html, 'lxml')  # 傳入第page_num頁的html
 90         li_list = li_soup.find_all('li', attrs={'class', 'image-item'})   # 找到li所在位置
 91         # print('get_list succeed')
 92         # print(li_list)
 93         for li in li_list:
 94             href = li.find('a')['href']  # 直接提取第一個href
 95             # print('get_href succeed')
 96             # print(href)
 97             jump_to_url = self.main_url + href  # 跳轉到目標的url
 98             # print('get_jump_to_url succeed')
 99             jump_to_html = self.get_html(jump_to_url, 3).text  # 獲取圖片的html
100             # print('get_jump_to_html succeed')
101 
102             img_soup = BeautifulSoup(jump_to_html, 'lxml')
103             img_info = img_soup.find('div', attrs={'class', 'works_display'})\
104                 .find('div', attrs={'class', '_layout-thumbnail ui-modal-trigger'})
105             # 找到目標位置的信息
106             if img_info is None:  # 有些找不到url,如果不continue會報錯
107                 continue
108             self.download_img(img_info, jump_to_url, page_num)  # 去下載這個圖片
109 
110     def download_img(self, img_info, href, page_num):
111         title = img_info.find('img')['alt']  # 提取標題
112         src = img_info.find('img')['src']  # 提取圖片位置
113         src_headers = self.headers
114         src_headers['Referer'] = href  # 增加一個referer,否則會403,referer就像上面登陸一樣找
115         try:
116             html = requests.get(src, headers=src_headers)
117             img = html.content
118         except:  # 有時候會發生錯誤導致不能獲取圖片.直接跳過這張圖吧
119             print('獲取該圖片失敗')
120             return False
121 
122         title = title.replace('?', '_').replace('/', '_').replace('\\', '_').replace('*', '_').replace('|', '_')\
123             .replace('>', '_').replace('<', '_').replace(':', '_').replace('"', '_').strip()
124         # 去掉那些不能在文件名里面的.記得加上strip()去掉換行
125 
126         if os.path.exists(os.path.join(self.load_path, str(page_num), title + '.jpg')):
127             for i in range(1, 100):
128                 if not os.path.exists(os.path.join(self.load_path, str(page_num), title + str(i) + '.jpg')):
129                     title = title + str(i)
130                     break
131         # 如果重名了,就加上一個數字
132         print('正在保存名字為: ' + title + ' 的圖片')
133         with open(title + '.jpg', 'ab') as f:  # 圖片要用b
134             f.write(img)
135         print('保存該圖片完畢')
136 
137     def mkdir(self, path):
138         path = path.strip()
139         is_exist = os.path.exists(os.path.join(self.load_path, path))
140         if not is_exist:
141             print('創建一個名字為 ' + path + ' 的文件夾')
142             os.makedirs(os.path.join(self.load_path, path))
143             os.chdir(os.path.join(self.load_path, path))
144             return True
145         else:
146             print('名字為 ' + path + ' 的文件夾已經存在')
147             os.chdir(os.path.join(self.load_path, path))
148             return False
149 
150     def work(self):
151         self.login()
152         for page_num in range(1, 51):  # 太多頁了,只跑50頁
153             path = str(page_num)  # 每一頁就開一個文件夾
154             self.mkdir(path)  # 創建文件夾
155             # print(self.target_url + str(page_num))
156             now_html = self.get_html(self.target_url + str(page_num), 3)  # 獲取頁碼
157             self.get_img(now_html.text, page_num)  # 獲取圖片
158             print('第 {page} 頁保存完畢'.format(page=page_num))
159             time.sleep(2)  # 防止太快被反
160 
161 
162 pixiv = Pixiv()
163 pixiv.work()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM