2026-01-24 17:07:34 +08:00

285 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from playwright.sync_api import sync_playwright, BrowserContext, Page
import time
import json
import os
from typing import List, Dict, Optional
from company_spider.zhilianzhaopin_company.searcc_kw import generate_url
class CityLoader:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(CityLoader, cls).__new__(cls)
return cls._instance
def __init__(self, city_file="city.json"):
if hasattr(self, 'city_map'):
return
current_dir = os.path.dirname(os.path.abspath(__file__))
self.file_path = os.path.join(current_dir, city_file)
self.city_map = {}
self._load_cities()
def _load_cities(self):
if not os.path.exists(self.file_path):
print(f"City file not found: {self.file_path}")
return
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
self._parse_city_data(data.get("allCity", []))
except Exception as e:
print(f"Error loading city file: {e}")
def _parse_city_data(self, cities):
for city in cities:
self.city_map[city['name']] = city['code']
if 'sublist' in city and city['sublist']:
self._parse_city_data(city['sublist'])
def get_code(self, city_name):
return self.city_map.get(city_name)
def get_companies_from_page(page: Page) -> List[Dict[str, str]]:
"""从搜索结果页面获取公司名称和链接"""
companies = []
# 尝试多种选择器来定位公司名称
company_selectors = [
'a[class*="company"]',
'.company-name a',
'a.company-name',
'[class*="CompanyName"] a',
'a[href*="/company/"]'
]
company_elements = []
for selector in company_selectors:
try:
elements = page.query_selector_all(selector)
if elements:
company_elements = elements
print(f"使用选择器找到 {len(elements)} 个元素: {selector}")
break
except:
continue
# 如果没找到,尝试更通用的方法
if not company_elements:
all_links = page.query_selector_all('a[href*="company"]')
company_elements = all_links
print(f"通过通用方法找到 {len(all_links)} 个公司链接")
# 提取公司信息
company_info_set = set()
for element in company_elements:
try:
company_name = element.inner_text().strip()
company_url = element.get_attribute('href')
if company_name and company_url:
# 处理相对路径
if company_url.startswith('/'):
company_url = f"https://www.zhaopin.com{company_url}"
elif not company_url.startswith('http'):
company_url = f"https://www.zhaopin.com/{company_url}"
# 去重
if company_name not in company_info_set:
company_info_set.add(company_name)
companies.append({
'name': company_name,
'url': company_url
})
except Exception as e:
continue
return companies
def get_company_intro(context: BrowserContext, company_url: str) -> str:
"""获取公司详情简介"""
try:
company_page = context.new_page()
company_page.goto(company_url, wait_until="networkidle", timeout=30000)
time.sleep(2)
# 尝试多种选择器获取公司简介
intro_selectors = [
'.company-intro',
'.company-description',
'[class*="intro"]',
'[class*="description"]',
'.company-info',
'[class*="CompanyIntro"]'
]
company_intro = ""
for selector in intro_selectors:
try:
intro_element = company_page.query_selector(selector)
if intro_element:
company_intro = intro_element.inner_text().strip()
if company_intro:
break
except:
continue
# 如果还是没找到,尝试获取页面主要内容
if not company_intro:
try:
body = company_page.query_selector('body')
if body:
all_text = body.inner_text()
company_intro = all_text[:500]
except:
pass
company_page.close()
return company_intro if company_intro else "未找到公司简介"
except Exception as e:
return f"获取失败: {str(e)}"
def crawl_companies(params: Dict, max_companies: int = 10, headless: bool = False, proxy: Optional[str] = None) -> List[Dict]:
"""
爬取智联招聘公司信息
Args:
params: 搜索参数,如 {'jl': 530, 'kw': 'app推广经理'} 或 {'city': '北京', 'kw': '...'}
max_companies: 最多爬取的公司数量默认10
headless: 是否无头模式默认False
proxy: 代理地址,例如 "http://user:pass@host:port"
Returns:
公司信息列表,每个元素包含 name, url, intro
如果找到完全匹配的公司名称只返回该公司的信息列表长度为1
"""
# 处理城市名称转代码
if 'city' in params and 'jl' not in params:
city_loader = CityLoader()
code = city_loader.get_code(params['city'])
if code:
print(f"城市 '{params['city']}' 映射代码为: {code}")
params['jl'] = code
else:
print(f"未找到城市 '{params['city']}' 的代码")
target_company = params.get('kw', '').strip()
with sync_playwright() as p:
launch_args = ["--disable-blink-features=AutomationControlled"]
browser_kwargs = {
"headless": headless,
"args": launch_args
}
# 尝试使用本地Chrome如果不存在则使用默认浏览器
chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
if os.path.exists(chrome_path):
browser_kwargs["executable_path"] = chrome_path
if proxy:
browser_kwargs["proxy"] = {"server": proxy}
print(f"使用代理: {proxy}")
browser = p.chromium.launch(**browser_kwargs)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
page = context.new_page()
# 生成URL并访问搜索页面
url = f"https://www.zhaopin.com/sou/{generate_url(params)}"
print(f"访问URL: {url}")
page.goto(url, wait_until="networkidle", timeout=30000)
time.sleep(3)
# 获取公司列表
companies = get_companies_from_page(page)
print(f"\n找到 {len(companies)} 家公司")
# 如果有关键词,尝试精确匹配公司名称
if target_company:
print(f"搜索目标公司: {target_company}")
for company in companies:
company_name = company['name'].strip()
# 优先精确匹配,如果精确匹配失败则尝试包含匹配
if company_name == target_company:
print(f"找到完全匹配的公司: {company_name}")
print(f"正在获取公司简介...")
company_intro = get_company_intro(context, company['url'])
context.close()
browser.close()
return [{
'name': company_name,
'url': company['url'],
'intro': company_intro
}]
# 如果精确匹配失败,尝试包含匹配
for company in companies:
company_name = company['name'].strip()
if target_company in company_name or company_name in target_company:
print(f"找到部分匹配的公司: {company_name}")
print(f"正在获取公司简介...")
company_intro = get_company_intro(context, company['url'])
context.close()
browser.close()
return [{
'name': company_name,
'url': company['url'],
'intro': company_intro
}]
# 如果没有找到匹配的公司,按原逻辑获取多家公司
print(f"未找到完全匹配的公司,获取前 {max_companies} 家公司信息")
results = []
for i, company in enumerate(companies[:max_companies], 1):
print(f"\n[{i}/{min(max_companies, len(companies))}] 正在获取: {company['name']}")
company_intro = get_company_intro(context, company['url'])
results.append({
'name': company['name'],
'url': company['url'],
'intro': company_intro
})
time.sleep(1)
context.close()
browser.close()
return results
if __name__ == '__main__':
# 测试代码
params = {'city': '北京', 'kw': 'app推广经理'}
results = crawl_companies(params, max_companies=10)
# 输出结果
print("\n" + "="*80)
print("爬取结果:")
print("="*80)
for result in results:
print(f"\n公司名称: {result['name']}")
print(f"公司链接: {result['url']}")
print(f"公司简介: {result['intro'][:200]}..." if len(result['intro']) > 200 else f"公司简介: {result['intro']}")
print("-"*80)