文章目錄
爬蟲需求:
需要爬取的網站 目標網站
爬取內容:需要爬取網站的公司名稱以及電話
公司名稱在列表頁
聯繫方式在詳情頁
該網站有6000多條信息
1.考慮到網站信息太多,不能使用簡單的爬蟲請求頁面,需要考慮到單個ip無法滿足要求,網站訪問次數過多,會被網站反爬機制封ip
2.單個程序請求頁面,還要解析,單線程太慢
爬蟲思路解析
事先導入需要用的模塊
import random
import requests
from lxml import etree
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time
1.封裝函數獲取網站所有頁碼
def get_pageurl(start, page_len, url):
"""
:param start: 起止頁碼
:param page_len: 頁碼個數
:param url: 網站模板url
:return:
"""
print('****************開始獲取取頁面URL****************')
i = start
j = 0
url_list = []
while j <= page_len:
#網站頁碼規律 模板url http://shop.jc001.cn/r1-231/?p=
# j爲頁碼數 url_full= 'http://shop.jc001.cn/r1-231/?p=' +頁碼數
url_full = url + str(i)
url_list.append(url_full)
i += 1
j += 1
else:
print('****************已爬取頁面URL****************')
return url_list
2.通過獲取頁面的url去解析獲取每一個詳情頁的url
def get_allurls(url_list):
print('****************開始爬取商家頁面URL****************')
# 集合用來存儲不重複的所有詳情頁的url
all_urls = set()
count = 0
for page_url in url_list:
page_text = requests.get(url=page_url, headers=header,
proxies={"http": random.choice(proxy_ip)}).text
tree = etree.HTML(page_text)
#解析每個頁面所包含的詳情頁的url
a_list = tree.xpath('/html/body/div[8]/div[2]/div[2]/table/tbody/tr/td[1]/a/@href')
for a in a_list:
# a = "http:" + a
all_urls.add(a)
count += 1
print('****************已爬取 %s 家商家頁面URL****************' % count)
return all_urls
3.網站詳情頁請求以及解析
首先網站詳情頁6000多張 ,不可能網站一張張的去請求,這樣花費時間非常多,而且由於ip的訪問頻率限制,在請求的時候還需要設置請求間隔,以防單個ip請求速度過快導致ip被封,所以這裏需要用到多線程
3.1詳情頁請求函數
這裏注意一點,一定要使用異常模塊,想一想發生異常就要拋出一面紅看着就難受,所以這裏使用異常模塊,單個線程執行速度很快
time.sleep(0.5) random.choice(proxy_ip) 都是是爲了防止單個ip每秒內訪問服務器次數太多導致ip不可用。
def get_page(detail_url):
time.sleep(0.5)
try:
response = requests.get(detail_url, headers=header, proxies={"http": random.choice(proxy_ip)}, timeout=5).text
except Exception as e:
response = ' '
return {'url': detail_url, 'text': response}
3.2詳情頁解析函數(解析需要內容,並存儲)
def parase_page(res):
# parse_page拿到的是一個future對象obj,需要用obj.result()拿到結果
res = res.result()
page_text = res.get('text')
try:
if page_text:
tree = etree.HTML(page_text)
name = tree.xpath('/html/body/div[2]/div[1]/div[1]/div/h3/a/text()')[0]
num = tree.xpath('//div[@class="cnt line"]/table//tr[3]/td//text()')[0].strip('\xa0')
with open('xiaogan.txt', 'a', encoding='utf-8') as f:
f.write('%s|%s\n' % (name, num))
except Exception as e:
print(e)
3.3詳情頁解析總函數
def get_information(all_urls):
print('****************開始爬取所有公司名稱電話****************')
#線性池 15個線程同時執行
p = ThreadPoolExecutor(15)
for detail_url in all_urls:
#add_done_callback(parase_page) 回調函數 執行完請求就回調解析模塊
p.submit(get_page,detail_url).add_done_callback(parase_page)
UA僞裝以及代理IP
代理ip是在快代理上購買的私密ip
#UA僞裝
header = {
"user-agent": "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
'Connection': 'close',
"Accept-Encoding": "Gzip",
}
#代理ip
#proxy_ip是一個包含30個代理ip的列表
api_url = "http://dps.kdlapi.com/api/getdps/?orderid=958623468831485&num=30&pt=1&format=json&sep=1"
proxy_ip = requests.get(api_url).json()['data']['proxy_list']
調用函數開始執行
url = "http://shop.jc001.cn/r1-239/?p="
url_list = get_pageurl(1,49, url)
all_urls = get_allurls(url_list)
print(all_urls)
get_information(all_urls)
最終結果
上面程序是運行一次可以爬取1000條資料,運行速度也很快,15線程同時運行
從代理ip網站後臺可以看到程序的響應數很高