之前在写一个站点,专门采集CSDN上面的问题,测试了一天可以采集几十万条问题,几万条答案。不过采集时间过长容易出现CSDN封IP半小时。现在把源码放上来供大家学习研究。。。
ItHtwDlg.h
ItHtwDlg.cpp
项目源码:ItHtw_NEW
ItHtwDlg.h
// ItHtwDlg.h : 头文件 // #pragma once #include #include "Item.h" class CType{ public: int id; CString text; }; // CItHtwDlg 对话框 class CItHtwDlg : public CDialogEx { // 构造 public: CArray<CType*> types; CItHtwDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_ITHTW_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: static UINT SQLThreadFunc(LPVOID pVoid); afx_msg void OnBnClickedButtonDisconnect(); afx_msg void OnBnClickedButtonData(); afx_msg void OnBnClickedButtonGetTotalPage(); afx_msg void OnClose(); afx_msg void OnBnClickedButtonDataStop(); void GetPageListContent(void); virtual BOOL PreTranslateMessage(MSG* pMsg); bool m_thread; bool m_cThread; CString m_log; std::vector<CItem*> _vecItems; afx_msg void OnBnClickedButtonClear(); afx_msg void OnBnClickedButtonContentStart(); afx_msg void OnBnClickedButtonContentStop(); afx_msg void OnBnClickedButtonGetTotalPage2(); afx_msg void OnDestroy(); afx_msg void OnBnClickedButtonGetTotalPage3(); void CheckConnect(bool tip=false); afx_msg void OnBnClickedButtonDisconnect2(); static UINT SQLThreadFuncContent(LPVOID pVoid); void GetContent(void); afx_msg void OnBnClickedButtonContentStop2(); void ClearVector(void); afx_msg void OnBnClickedButtonGetTotalPlatform(); afx_msg void OnCbnSelchangeComboPlatform(); afx_msg void OnEnKillfocusEditPageStart(); };
ItHtwDlg.cpp
// ItHtwDlg.cpp : 实现文件 // #include "stdafx.h" #include "ItHtw.h" #include "ItHtwDlg.h" #include "afxdialogex.h" #include "Csdn.h" #include "CsdnPage.h" #include "MysqlHelper.h" #ifdef _DEBUG #define new DEBUG_NEW #endif char db_host[32] = "localhost"; char db_base[32] = "test"; char db_user[32] = "root"; char db_pwd[32] = "123456"; char db_total[12]; char db_beginID[12]; char db_type[3]; int _total = 100000; int _beginID = 1; int _type = 1; // CItHtwDlg 对话框 CItHtwDlg::CItHtwDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CItHtwDlg::IDD, pParent) , m_thread(false) , m_cThread(false) , m_log(_T("")) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CItHtwDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_CONTENT, m_log); } BEGIN_MESSAGE_MAP(CItHtwDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &CItHtwDlg::OnBnClickedButtonDisconnect) ON_BN_CLICKED(IDC_BUTTON_DATA, &CItHtwDlg::OnBnClickedButtonData) ON_BN_CLICKED(IDC_BUTTON_GET_TOTAL_PAGE, &CItHtwDlg::OnBnClickedButtonGetTotalPage) ON_WM_CLOSE() ON_BN_CLICKED(IDC_BUTTON_DATA_STOP, &CItHtwDlg::OnBnClickedButtonDataStop) ON_BN_CLICKED(IDC_BUTTON_CLEAR, &CItHtwDlg::OnBnClickedButtonClear) ON_BN_CLICKED(IDC_BUTTON_CONTENT_START, &CItHtwDlg::OnBnClickedButtonContentStart) ON_BN_CLICKED(IDC_BUTTON_CONTENT_STOP, &CItHtwDlg::OnBnClickedButtonContentStop) ON_BN_CLICKED(IDC_BUTTON_GET_TOTAL_PAGE2, &CItHtwDlg::OnBnClickedButtonGetTotalPage2) ON_WM_DESTROY() ON_BN_CLICKED(IDC_BUTTON_GET_TOTAL_PAGE3, &CItHtwDlg::OnBnClickedButtonGetTotalPage3) ON_BN_CLICKED(IDC_BUTTON_DISCONNECT2, &CItHtwDlg::OnBnClickedButtonDisconnect2) ON_BN_CLICKED(IDC_BUTTON_CONTENT_STOP2, &CItHtwDlg::OnBnClickedButtonContentStop2) ON_BN_CLICKED(IDC_BUTTON_GET_TOTAL_PLATFORM, &CItHtwDlg::OnBnClickedButtonGetTotalPlatform) ON_CBN_SELCHANGE(IDC_COMBO_PLATFORM, &CItHtwDlg::OnCbnSelchangeComboPlatform) ON_EN_KILLFOCUS(IDC_EDIT_PAGE_START, &CItHtwDlg::OnEnKillfocusEditPageStart) END_MESSAGE_MAP() // CItHtwDlg 消息处理程序 BOOL CItHtwDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 SetDlgItemTextA(IDC_EDIT_ADDR,db_host); SetDlgItemTextA(IDC_EDIT_TABLE,db_base); SetDlgItemTextA(IDC_EDIT_NAME,db_user); SetDlgItemTextA(IDC_EDIT_PWD,db_pwd); sprintf_s(db_total,"%d",_total); sprintf_s(db_beginID,"%d",_beginID); sprintf_s(db_type,"%d",_type); SetDlgItemTextA(IDC_EDIT_BEGINID,db_beginID); SetDlgItemTextA(IDC_EDIT_TOTAL,db_total); SetDlgItemTextA(IDC_EDIT_CLASS,db_type); char temp[128]; GT_ReadUTF("csdn","page_begin",temp); if(strlen(temp) == 0) sprintf_s(temp,"%d",1); SetDlgItemTextA(IDC_EDIT_PAGE_START,temp); memset(temp,0,sizeof(temp)); GT_ReadUTF("csdn","page_end",temp); if(strlen(temp) == 0) sprintf_s(temp,"%d",1); SetDlgItemTextA(IDC_EDIT_PAGE_END,temp); //content memset(temp,0,sizeof(temp)); GT_ReadUTF("csdn_content","content_begin",temp); if(strlen(temp) == 0) sprintf_s(temp,"%d",0); SetDlgItemTextA(IDC_EDIT_PAGE_START2,temp); memset(temp,0,sizeof(temp)); GT_ReadUTF("csdn_content","content_count",temp); if(strlen(temp) == 0) sprintf_s(temp,"%d",1); SetDlgItemTextA(IDC_EDIT_PAGE_END2,temp); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CItHtwDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } void CItHtwDlg::OnClose() { // TODO: 在此添加消息处理程序代码和/或调用默认值 CDialogEx::OnClose(); m_sleep = false; ClearVector(); int nCount = types.GetSize(); for (int i=0;i<nCount;i++) { CType* type = types.GetAt(i); if(type != NULL) delete type; } CMysqlHelper::getInstance()->close(); } void CItHtwDlg::OnDestroy() { CDialogEx::OnDestroy(); // TODO: 在此处添加消息处理程序代码 } void CItHtwDlg::ClearVector(void) { int _vSize = _vecItems.size(); for (int i=0;i<_vSize;i++) { CItem* _item = _vecItems.at(i); if(_item != NULL) delete _item; _item = NULL; } _vecItems.clear(); } void CItHtwDlg::CheckConnect(bool tip) { if (!CMysqlHelper::getInstance()->isConnected()) { GetDlgItemText(IDC_EDIT_ADDR,db_host,sizeof(db_host)); GetDlgItemText(IDC_EDIT_TABLE,db_base,sizeof(db_base)); GetDlgItemText(IDC_EDIT_NAME,db_user,sizeof(db_user)); GetDlgItemText(IDC_EDIT_PWD,db_pwd,sizeof(db_pwd)); if(CMysqlHelper::getInstance()->init(db_host,db_base,db_user,db_pwd)&&tip) AfxMessageBox("连接成功"); } } BOOL CItHtwDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 if (pMsg->wParam == VK_ESCAPE||pMsg->wParam == VK_RETURN) return FALSE; //回车或空格返回FALSH,不阻塞后面操作 return CDialogEx::PreTranslateMessage(pMsg); } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CItHtwDlg::OnQueryDragIcon() { return static_cast(m_hIcon); } UINT CItHtwDlg::SQLThreadFunc(LPVOID pVoid) { CItHtwDlg* dlg = (CItHtwDlg*)pVoid; dlg->GetPageListContent(); return 0; } UINT CItHtwDlg::SQLThreadFuncContent(LPVOID pVoid) { CItHtwDlg* dlg = (CItHtwDlg*)pVoid; dlg->GetContent(); return 0; } void CItHtwDlg::GetPageListContent(void) { if(!CMysqlHelper::getInstance()->m_connected) { AfxMessageBox("请先连接数据库"); return; } CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); if(types.GetSize() == 0) { AfxMessageBox("平台数组未初始化"); return; } int _type = types.GetAt(box->GetCurSel())->id; CString platform = types.GetAt(box->GetCurSel())->text; GetDlgItem(IDC_BUTTON_DATA)->EnableWindow(FALSE); m_thread = true; char pageTotal[10]; GetDlgItemText(IDC_EDIT_PAGE_END,pageTotal,sizeof(pageTotal)); char pageStart[10]; GetDlgItemText(IDC_EDIT_PAGE_START,pageStart,sizeof(pageStart)); int nTotal = atoi(pageTotal); int nStart = atoi(pageStart); m_log = ""; CCsdn csdn; CCsdnPage csdnPage; long _begin = currentTimeSeconds(); long _use = -1; char csdn_page_begin[10]; int tempIndex = -1; long once_begin = -1; long once_use = -1; char once_title[128]; for (int i=nStart;m_thread && i<=nTotal;i++) { once_begin = currentTimeSeconds(); CString url; url.Format("http://bbs.csdn.net/forums/%s/closed?page=%d",platform,i); CString text = csdn.HttpGet(url); csdn.ParseList(text); int nCount = csdn._items.size(); while(nCount == 0) { CString tmp; tmp += url; tmp += " | 获取条目为0条,休息10分钟继续!"; SetWindowTextA(tmp); MSleep(1000*60*10);//休眠10分钟继续 text = csdn.HttpGet(url); csdn.ParseList(text); nCount = csdn._items.size(); SetWindowTextA(url); } sprintf_s(csdn_page_begin,"%d",i); GT_WriteUTF("csdn","page_begin",csdn_page_begin); SetDlgItemText(IDC_EDIT_PAGE_START,csdn_page_begin); for (int j=0;j<nCount;j++) { CItem* pItem = csdn._items.at(j); pItem->_type = _type; if(!csdnPage.InsertPage(pItem->_title,pItem->_url,_type,pItem->_reply)) { char error[512]; sprintf_s(error,"%d:%d->[%s]插入数据库失败!rnrnrn",i,j,pItem->_title); m_log += error; SetDlgItemText(IDC_EDIT_CONTENT,m_log); } delete pItem; pItem = NULL; } csdn._items.clear(); tempIndex = i; once_use = currentTimeSeconds() - once_begin; _use = currentTimeSeconds()-_begin; sprintf_s(once_title,"%s 本页花费:%d秒 总花费:%d秒",url,once_use,_use); SetWindowTextA(once_title); } if(!m_thread) { tempIndex++; sprintf_s(csdn_page_begin,"%d",tempIndex); GT_WriteUTF("csdn","page_begin",csdn_page_begin); } _use = currentTimeSeconds()-_begin; CString _title; _title.Format("采集任务完成 总花费:%d秒",_use); SetWindowTextA(_title); GetDlgItem(IDC_BUTTON_DATA)->EnableWindow(); } //获取内容 void CItHtwDlg::GetContent(void) { CCsdnPage csdnPage; CCsdn csdn; int nSize = _vecItems.size(); CString log = ""; CString title; long once_begin = -1; long once_use = -1; long _begin = currentTimeSeconds(); long _use = -1; char once_title[256]; char current_id[10]; GetDlgItemText(IDC_EDIT_PAGE_START2,current_id,sizeof(current_id)); int _beginID = atoi(current_id); m_cThread = true; int tempIndex = -1; bool mark = false; for (int i=0;i<nSize;i++) { tempIndex = i; once_begin = currentTimeSeconds(); CItem* _item = _vecItems.at(i); if(_item == NULL) continue; if (_item->_marka == 1) { delete _item; _item = NULL; continue; } if(m_cThread) { CString text = csdn.HttpGet(_item->_url); if (csdn.ParseQuestion(text,_item)) { //写入数据库 int _aSize = _item->_answer.size(); if (_aSize > 0) { //回复条目大于0 CString sql; sql.Format("insert into ithtw_answer(qid,answer,type)values(%d,'%s',%d)",_item->_pid,_item->_question,1); if(!CMysqlHelper::getInstance()->excute(sql.GetBuffer(0))) { CString tmp; tmp.Format("数据库写入失败:[%d->%s]nn",_item->_pid,_item->_title); GT_WriteReleaseLog(tmp,"C:csdn_content.log"); } } for (int j=1;j<_aSize;j++) { CAnswer* _answer = _item->_answer.at(j); if(_answer == NULL) continue; if(_answer->_text.Find("[/cpp] <div %s',%d)",pid,answer,score); if(!CMysqlHelper::getInstance()->excute(sql.GetBuffer(0))) { CString tmp; tmp.Format("数据库写入失败:[%d->%s]",_item->_pid,_item->_title); log += tmp; log += "nn"; GT_WriteReleaseLog(tmp,"C:csdn_content.log"); } } } //更新数据库 if(!csdnPage.UpdatePageMarkA(_item->_pid)) { CString tmp; tmp.Format("数据库更新失败:[%d->%s]",_item->_pid,_item->_title); GT_WriteReleaseLog(tmp); } } else { SetWindowTextA("解析出错或者被禁止访问了"); GT_WriteReleaseLog("解析出错或者被禁止访问了","C:csdn_content.log"); GT_WriteReleaseLog(_item->_title,"C:csdn_content.log"); //解析出错或者被禁止访问了 MSleep(1000*60*10);//休眠10分钟继续 i--;//回档 continue; } sprintf_s(current_id,"%d",_beginID+i+1); SetDlgItemText(IDC_EDIT_PAGE_START2,current_id); GT_WriteUTF("csdn_content","content_begin",current_id); SetDlgItemText(IDC_EDIT_CONTENT2,log); once_use = currentTimeSeconds() - once_begin; _use = currentTimeSeconds()-_begin; sprintf_s(once_title,"%s 本页花费:%d秒 总花费:%d秒",_item->_title,once_use,_use); SetWindowTextA(once_title); } else { if(!mark) { tempIndex++; sprintf_s(current_id,"%d",_beginID+tempIndex+1); SetDlgItemText(IDC_EDIT_PAGE_START2,current_id); GT_WriteUTF("csdn_content","content_begin",current_id); mark = true; } } _item->Clear(); delete _item; _item = NULL; if(i > 0 && i % 500 == 0) { SetWindowTextA("采集满500条休眠10分钟"); MSleep(1000*60*10);//休眠10分钟继续 } } _vecItems.clear(); sprintf_s(once_title,"总花费:%d秒",_use); SetWindowTextA(once_title); } void CItHtwDlg::OnBnClickedButtonClear() { // TODO: 在此添加控件通知处理程序代码 m_log = ""; SetDlgItemText(IDC_EDIT_CONTENT,m_log); } void CItHtwDlg::OnBnClickedButtonDisconnect() { // TODO: 在此添加控件通知处理程序代码 this->CheckConnect(true); } void CItHtwDlg::OnBnClickedButtonDisconnect2() { // TODO: 在此添加控件通知处理程序代码 CMysqlHelper::getInstance()->close(); } void CItHtwDlg::OnBnClickedButtonGetTotalPage() { // TODO: 在此添加控件通知处理程序代码 GetDlgItem(IDC_BUTTON_GET_TOTAL_PAGE)->EnableWindow(FALSE); CCsdn csdn; char platform[256] = "android"; CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); box->GetLBText(box->GetCurSel(),platform); CString url; url.Format("http://bbs.csdn.net/forums/%s/closed",platform); CString text = csdn.HttpGet(url); int _totalPageBegin = text.Find("尾页"); if(_totalPageBegin != -1 && _totalPageBegin >= 50) { CString str = text.Mid(_totalPageBegin-50,50); CString _value = csdn.ParseTag(str,"page=","""); SetDlgItemText(IDC_EDIT_PAGE_END,_value); GT_WriteUTF("csdn","page_end",_value); GT_WriteUTF("csdn","platform",platform); } GetDlgItem(IDC_BUTTON_GET_TOTAL_PAGE)->EnableWindow(); } void CItHtwDlg::OnBnClickedButtonData() { // TODO: 在此添加控件通知处理程序代码 GetDlgItemText(IDC_EDIT_ADDR,db_host,sizeof(db_host)); GetDlgItemText(IDC_EDIT_TABLE,db_base,sizeof(db_base)); GetDlgItemText(IDC_EDIT_NAME,db_user,sizeof(db_user)); GetDlgItemText(IDC_EDIT_PWD,db_pwd,sizeof(db_pwd)); m_sleep = true; AfxBeginThread(SQLThreadFunc,this); } void CItHtwDlg::OnBnClickedButtonDataStop() { // TODO: 在此添加控件通知处理程序代码 m_thread = false; m_sleep = false; GetDlgItem(IDC_BUTTON_DATA)->EnableWindow(); } void CItHtwDlg::OnBnClickedButtonContentStart() { // TODO: 在此添加控件通知处理程序代码 m_sleep = true; AfxBeginThread(SQLThreadFuncContent,this); } void CItHtwDlg::OnBnClickedButtonContentStop() { // TODO: 在此添加控件通知处理程序代码 m_cThread = false; m_sleep = false; } void CItHtwDlg::OnBnClickedButtonContentStop2() { // TODO: 在此添加控件通知处理程序代码 SetDlgItemText(IDC_EDIT_CONTENT2,""); } void CItHtwDlg::OnBnClickedButtonGetTotalPage2() { // TODO: 在此添加控件通知处理程序代码 this->CheckConnect(); char _begin[10]; char _count[10]; GetDlgItemText(IDC_EDIT_PAGE_START2,_begin,sizeof(_begin)); GetDlgItemText(IDC_EDIT_PAGE_END2,_count,sizeof(_count)); GT_WriteUTF("csdn_content","content_count",_count); CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); if (strlen(_begin) == 0 || strlen(_count) == 0 || box->GetCurSel() == -1) { AfxMessageBox("内容不能为空"); return; } int _type = types.GetAt(box->GetCurSel())->id; char sql[512]; sprintf_s(sql,"select * from ithtw_page where type=%d and id>=%d limit %d",_type,atoi(_begin),atoi(_count)); ClearVector(); std::vector<CStringArray*> _vector; CMysqlHelper::getInstance()->query(sql,_vector); int _vSize = _vector.size(); for (int i=0;i<_vSize;i++) { CStringArray* _array = _vector.at(i); CItem* _item = new CItem; if (_array != NULL) { int _aSize = _array->GetCount(); for (int j=0;j<_aSize;j++) { CString text = _array->GetAt(j); switch(j) { case 0: //pid _item->_pid = atoi(text); break; case 1: //title _item->_title = text; break; case 2: //url _item->SetUrl(text); break; case 3: //reply _item->_reply = atoi(text); break; case 4: //platform _item->_type = atoi(text); break; case 8: //mark answer _item->_marka = atoi(text); break; } } delete _array; _array = NULL; } _vecItems.push_back(_item); } MyMessageBox("成功读取%d条数据!",_vSize); } void CItHtwDlg::OnBnClickedButtonGetTotalPage3() { // TODO: 在此添加控件通知处理程序代码 this->CheckConnect(); CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); int _type = types.GetAt(box->GetCurSel())->id;; char sql[512]; sprintf_s(sql,"select count(*) from ithtw_page where type=%d",_type); std::vector<CStringArray*> _vector; CMysqlHelper::getInstance()->query(sql,_vector); int _vSize = _vector.size(); for (int i=0;i<_vSize;i++) { CStringArray* _array = _vector.at(i); if(_array != NULL) { SetDlgItemText(IDC_EDIT_PAGE_END2,_array->GetAt(0)); GT_WriteUTF("csdn_content","content_count",_array->GetAt(0)); delete _array; _array = NULL; } } } void CItHtwDlg::OnBnClickedButtonGetTotalPlatform() { // TODO: 在此添加控件通知处理程序代码 this->CheckConnect(); char sql[512] = "select * from ithtw_type"; std::vector<CStringArray*> _vector; CMysqlHelper::getInstance()->query(sql,_vector); int _vSize = _vector.size(); CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); box->ResetContent(); for (int i=0;i<_vSize;i++) { CStringArray* _array = _vector.at(i); CType* ctype = new CType; for (int j=0;jGetSize();j++) { //添加到列表中 if(j == 2)//只添加收录的内容 box->AddString(_array->GetAt(j)); if(j == 0) { //id ctype->id = atoi(_array->GetAt(j)); } else if(j == 2) { //text ctype->text = _array->GetAt(j); } } types.Add(ctype); delete _array; } box->SetCurSel(0); char platform[256] = "android"; box->GetLBText(box->GetCurSel(),platform); SetDlgItemText(IDC_EDIT_PLATFORM2,platform); } void CItHtwDlg::OnCbnSelchangeComboPlatform() { // TODO: 在此添加控件通知处理程序代码 char platform[256] = "android"; CComboBox *box = (CComboBox*)GetDlgItem(IDC_COMBO_PLATFORM); box->GetLBText(box->GetCurSel(),platform); SetDlgItemText(IDC_EDIT_PLATFORM2,platform); } void CItHtwDlg::OnEnKillfocusEditPageStart() { // TODO: 在此添加控件通知处理程序代码 char pageStart[10]; GetDlgItemText(IDC_EDIT_PAGE_START,pageStart,sizeof(pageStart)); int nStart = atoi(pageStart); if(nStart <= 0) { SetDlgItemText(IDC_EDIT_PAGE_START,"1"); MessageBox("数字必须大于0"); } }
项目源码:ItHtw_NEW
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2302
- 用户1336
- 访客10968998
每日一句
Qingming Festival invites us to honor ancestors with quiet reflection and respect.
清明节邀请我们以静思与敬意祭奠祖先。
清明节邀请我们以静思与敬意祭奠祖先。
新会员