>  기사  >  백엔드 개발  >  Python을 사용하여 Baidu Tieba 이메일 주소 및 휴대폰 번호를 캡처하는 다중 스레드 크롤러 작성

Python을 사용하여 Baidu Tieba 이메일 주소 및 휴대폰 번호를 캡처하는 다중 스레드 크롤러 작성

巴扎黑
巴扎黑원래의
2017-03-18 10:48:221539검색

설날은 다들 어떻게 보내셨는지 모르겠네요. 아무튼 칼럼 주인님이 하루 자고 일어나보니 QQ에 로그인을 했더니 누가 소스코드 복사를 해달라고 하더라구요. 티에바 크롤러 연습할 때 캐치를 썼던 기억이 나네요. 바이두 티에바 포스팅 기록에서 이메일 주소와 휴대폰 번호를 가져와서 누구나 배우고 참고할 수 있도록 오픈소스로 공유하는 크롤러입니다.

요구사항 분석:

이 크롤러는 주로 Baidu Tieba의 다양한 게시물의 내용을 크롤링하고, 게시물의 내용을 분석하여 휴대폰 번호와 이메일 주소를 추출합니다. 주요 흐름은 코드 주석에 자세히 설명되어 있습니다.

테스트 환경:

코드는 Windows7 64bit, python 2.7 64bit(mysqldb 확장자 설치) 및 centos 6.5, python 2.7(mysqldb 확장자 포함) 환경에서 테스트되었으며

환경 준비:

일을 잘하려면 먼저 도구를 갈고 닦아야 합니다. 스크린샷을 보면 알 수 있습니다. 내 환경은 Windows 7 + PyCharm입니다. 내 Python 환경은 Python 2.7 64비트입니다. 이것은 초보자에게 더 적합한 개발 환경입니다. 그런 다음 easy_install을 설치하는 것이 좋습니다. 이름에서 알 수 있듯이 이것은 일부 확장 패키지를 설치하는 데 사용됩니다. 예를 들어 Python에서 mysql 데이터베이스를 작동하려는 경우 Python은 기본적으로 이를 지원하지 않습니다. Python이 mysql 데이터베이스를 작동할 수 있도록 mysqldb 패키지가 설치되어 있어야 합니다. easy_install이 있으면 mysqldb 확장 패키지를 빠르게 설치하려면 한 줄의 명령만 있으면 됩니다. - 우분투를 편리하게 이용하세요.

관련 도구는 내 github: cw1997/python-tools에서 찾을 수 있습니다. easy_install을 설치하려면 Python 명령줄에서 py 스크립트를 실행하고 잠시 기다리면 자동으로 Windows에 연결됩니다. 환경 변수, Windows 명령줄에 easy_install을 입력하고 에코가 있으면 설치가 성공한 것입니다.

환경 선택 세부 사항:

컴퓨터 하드웨어의 경우 물론 속도가 빠를수록 좋습니다. 최소 8G의 메모리부터 시작하세요. 크롤러 자체가 많은 양의 데이터를 저장하고 구문 분석해야 하기 때문입니다. 중간 데이터, 특히 다중 스레드 크롤러 페이징을 사용하여 목록 및 세부정보 페이지를 크롤링하고 크롤링할 데이터의 양이 많은 경우 대기열을 사용하여 크롤링 작업을 할당하면 많은 메모리를 차지하게 됩니다. 때로는 우리가 캡처하는 데이터가 json을 사용하는 경우도 있습니다. mongodb와 같은 nosql 데이터베이스에 저장되면 많은 메모리를 차지하게 됩니다.

시중에 판매되는 일부 열악한 무선 라우터 및 일반 민간 무선 네트워크 카드에서는 스레드 연결 시 간헐적인 네트워크 연결 끊김, 데이터 손실, 패킷 삭제 등이 발생할 수 있으므로 네트워크 연결에 유선 네트워크를 사용하는 것이 좋습니다. 나는 이것을 개인적으로 알고 있습니다.

운영체제와 Python은 당연히 64비트를 선택하세요. 32비트 운영 체제를 사용하는 경우 대용량 메모리를 사용할 수 없습니다. 32비트 파이썬을 사용하는 경우 소규모로 데이터를 캡쳐할 때에는 별 문제가 없을 수 있지만, 많은 양의 데이터를 저장하는 리스트, 큐, 딕셔너리 등 데이터의 양이 커지면 문제가 발생합니다. 원인 Python의 메모리 사용량이 2g를 초과하면 메모리 오버플로 오류가 보고됩니다. 그 이유는 내가 한때 Segmentfault에 대해 물었던 질문에 대한 Evian의 답변에 설명되어 있습니다(java-python httplib 모듈은 메모리 사용량이 1.9G에 도달하자마자 메모리 오버플로 오류를 보고하기 시작합니다 - SegmentFault)

mysql을 사용하여 데이터를 저장하려면 mysql5.5 이상 버전을 사용하는 것이 좋습니다. mysql5.5 버전은 json 데이터 유형을 지원하므로 mongodb를 포기할 수 있습니다. (어떤 사람들은 mysql이 mongodb보다 더 안정적이라고 하는데 저는 잘 모르겠습니다.)

Python은 이제 버전 3.x도 나와 있는데 왜 여기서는 아직도 python2.7을 사용하고 있는 걸까요? 개인적으로 버전 2.7을 선택한 이유는 오래전에 구매한 파이썬 코어 프로그래밍 책이 2판인데, 아직도 예제 버전으로 2.7을 사용하고 있기 때문입니다. 그리고 인터넷에는 2.7을 버전으로 사용하는 튜토리얼 자료가 여전히 많이 있습니다. 2.7은 일부 측면에서 3.x와 여전히 매우 다릅니다. 2.7을 연구하지 않았다면 발생할 수 있는 미묘한 구문 차이를 이해하지 못할 수도 있습니다. 우리의 이해에 차이가 있거나 데모 코드를 이해할 수 없습니다. 그리고 버전 2.7과만 호환되는 일부 종속 패키지도 있습니다. 내 제안은 당신이 파이썬을 배우고 회사에서 일하고 싶고 회사에 유지 관리할 오래된 코드가 없다면 시간이 충분하고 여유가 없다면 3.x를 직접 시작하는 것을 고려할 수 있다는 것입니다. 매우 체계적인 전문가가 주도하기 때문에 인터넷에 흩어져 있는 블로그 글로만 학습할 수 있으므로 2.7을 먼저 배우고 3.x를 배우는 것이 좋습니다. 결국 2.7을 학습한 후 3.x를 빨리 시작할 수 있습니다. .

멀티 스레드 크롤러와 관련된 지식 포인트:

사실 어떤 소프트웨어 프로젝트에서든 이 프로젝트를 작성하는 데 필요한 지식 포인트가 무엇인지 알고 싶다면 주요 포인트를 관찰하면 됩니다. 이 프로젝트의 항목 파일로 가져오는 패키지는 무엇입니까?

HTTP 프로토콜:

기본적으로 당사 크롤러는 지속적으로 http 요청을 시작하고 http 응답을 얻은 후 컴퓨터에 저장하여 데이터를 캡처합니다. http 프로토콜을 이해하면 연결 유지 등과 같이 데이터를 크롤링할 때 크롤링 속도를 높일 수 있는 일부 매개변수를 정확하게 제어하는 ​​데 도움이 됩니다.

스레딩 모듈(멀티스레딩):

우리가 일반적으로 작성하는 프로그램은 단일 스레드 프로그램입니다. 우리가 작성하는 코드는 모두 메인 스레드에서 실행되며, 이 메인 스레드는 Python에서 실행됩니다. 프로세스. . 스레드와 프로세스에 대한 설명은 Ruan Yifeng의 블로그를 참조하세요. 프로세스와 스레드에 대한 간단한 설명 - Ruan Yifeng의 온라인 로그

Python의 멀티스레딩은 스레딩이라는 모듈을 통해 구현됩니다. 이전에도 스레드 모듈이 있었지만 스레딩은 스레드에 대한 제어가 더 강력했기 때문에 나중에 멀티 스레드 프로그래밍을 구현하기 위해 스레딩으로 전환했습니다.

스레딩 및 멀티스레딩의 일부 용도에 대해서는 다음 기사가 좋다고 생각합니다. [python] 주제 8. 멀티스레드 프로그래밍의 스레드 및 스레딩 참고하시면 됩니다.

간단히 말하면 스레딩 모듈을 사용하여 멀티스레드 프로그램을 작성하려면 먼저 클래스를 직접 정의한 다음 이 클래스가 threading.Thread를 상속하고 각 작업 코드를 작성해야 합니다. 물론 스레드가 생성될 때 스레드 자체가 일부 초기화 작업을 수행해야 하는 경우 초기화 작업을 위해 실행될 코드는 __init__ 메서드에 작성되어야 합니다. PHP와 Java의 생성자 메소드.

여기서 추가적으로 이야기할 점은 스레드 안전성의 개념입니다. 일반적으로 단일 스레드 상황에서는 매번 하나의 스레드만 리소스(파일, 변수)에 대해 작동하므로 충돌이 발생할 가능성이 없습니다. 그러나 멀티스레딩의 경우 두 개의 스레드가 동시에 동일한 리소스를 작동하여 리소스 손상을 일으킬 수 있으므로 일반적으로 잠금 및 기타 작업과 관련된 이러한 충돌로 인한 손상을 해결하기 위한 메커니즘이 필요합니다. 예를 들어, mysql 데이터베이스의 innodb 테이블 엔진에는 행 수준 잠금 등이 있고 파일 작업에는 읽기 잠금 등이 있습니다. 이러한 작업은 모두 프로그램의 최하위 계층에서 완료됩니다. 따라서 일반적으로 해당 작업이나 스레드 안전 문제를 다루는 프로그램만 알면 멀티 스레드 프로그래밍에 사용할 수 있습니다. 스레드 안전성 문제를 고려한 이러한 종류의 프로그램을 일반적으로 "스레드 안전성 버전"이라고 합니다. 예를 들어 PHP에는 TS 버전이 있는데 이 TS는 스레드 안전성을 의미합니다. 아래에서 이야기할 Queue 모듈은 스레드로부터 안전한 대기열 데이터 구조이므로 멀티 스레드 프로그래밍에서 자신있게 사용할 수 있습니다.

마지막으로 스레드 차단의 중요한 개념에 대해 이야기하겠습니다. 스레딩 모듈을 자세히 연구한 후에는 스레드를 생성하고 시작하는 방법을 알게 될 것입니다. 그러나 스레드를 생성한 다음 start 메서드를 호출하면 전체 프로그램이 즉시 종료된다는 것을 알 수 있습니다. 무슨 일이 일어나고 있는 걸까요? 실제로 이는 메인 스레드에 하위 스레드를 시작하는 코드만 있기 때문입니다. 즉, 하위 스레드가 실행하는 코드는 메인 스레드에 하위 스레드를 시작하는 기능만 있다는 뜻입니다. , 이는 본질적으로 클래스에 작성된 메소드일 뿐입니다. 실제로 메인 스레드에서 실행되지 않으므로 메인 스레드가 하위 스레드를 시작한 후 모든 작업이 완료되고 훌륭하게 종료됩니다. 이제 메인 스레드가 종료되었으므로 Python 프로세스가 종료되고 다른 스레드에는 실행을 계속할 메모리 공간이 없습니다. 따라서 모든 하위 스레드 형제가 성공적으로 종료될 때까지 메인 스레드 형제가 기다리도록 해야 합니다. 그러면 스레드 개체에 메인 스레드를 방해할 수 있는 방법이 있습니까? 스레드.잠? 이것은 실제로 해결책입니다. 하지만 메인 스레드는 얼마나 오랫동안 휴면 상태여야 합니까? 작업을 완료하는 데 시간이 얼마나 걸릴지 정확히 알 수 없으므로 이 방법을 사용할 수 없습니다. 그렇다면 지금은 하위 스레드를 메인 스레드에 "고착"시킬 수 있는 방법이 있는지 온라인으로 확인해야 할까요? "stuck"이라는 단어는 너무 천박해 보입니다. 사실 좀 더 전문적으로 말하면 "blocking"이라고 해야 합니다. 따라서 검색 엔진을 올바르게 사용하면 "python sub-thread가 메인 스레드를 차단합니다"라고 쿼리할 수 있습니다. Join()이라는 메소드를 찾으십시오. 예, 이 Join() 메소드는 하위 스레드가 실행을 완료하지 못한 경우 하위 스레드에서 사용되는 메소드입니다. Join() 메서드가 포함된 줄에 도달하면 모든 스레드의 실행이 완료될 때까지 Join() 메서드 다음의 코드가 실행되지 않습니다.

큐 모듈(queue):

어떤 사람의 블로그를 크롤링해야 하는 시나리오가 있다고 가정해 보겠습니다. 우리는 이 사람의 블로그에 두 페이지가 있고 list.php 페이지에 모든 기사 링크가 표시된다는 것을 알고 있습니다. 이 블로그와 view.php 페이지는 기사의 특정 내용을 표시합니다.

이 사람의 블로그에 있는 모든 기사 콘텐츠를 크롤링하려는 경우 ​​​​단일 스레드 크롤러를 작성하는 아이디어는 다음과 같습니다. 먼저 정규식을 사용하여 모든 블로그의 a 태그의 href 속성을 캡처합니다. 이 list.php 페이지의 링크를 꺼내서 item_list라는 배열에 저장한 다음(파이썬에서는 배열이라고 하지 않고 목록, 중국어 이름 목록이라고 합니다) for 루프를 사용하여 item_list를 순회합니다. 배열하고 다양한 기능을 사용하여 웹 페이지 콘텐츠를 캡처하여 데이터베이스에 저장합니다.

프로그램이 10개의 스레드를 사용한다고 가정하고 이 작업을 완료하기 위해 다중 스레드 크롤러를 작성하려면 이전에 크롤링된 기사 목록을 각각 10개의 부분으로 나누는 방법을 찾아야 합니다. 하위 스레드 중 하나에 할당됩니다.

그러나 여기에 문제가 있습니다. 기사 목록 배열의 길이가 10의 배수가 아닌 경우, 즉 기사 수가 10의 정수 배수가 아닌 경우 마지막 스레드에는 더 적은 작업이 할당됩니다. 다른 스레드보다 빨리 끝나겠죠.

단 몇 천 단어로 이런 블로그 기사를 크롤링하면 별 문제가 없어 보이지만 작업(반드시 웹 페이지를 크롤링하는 작업은 아니지만 수학적 계산일 수 있음)이 있는 경우 , 또는 그래픽 렌더링 시간이 많이 걸리는 작업(예: 시간이 많이 걸리는 작업)의 실행 시간이 매우 길면 리소스와 시간이 크게 낭비됩니다. 멀티스레딩의 목적은 모든 컴퓨팅 리소스와 컴퓨팅 시간을 최대한 활용하는 것이므로 보다 과학적이고 합리적으로 작업을 할당하는 방법을 찾아야 합니다.

그리고 상황도 고려해야 하는데, 즉 기사 수가 많을 때 기사 콘텐츠를 빠르게 크롤링하여 최대한 빨리 크롤링한 콘텐츠를 볼 수 있어야 합니다. 이러한 종류의 수요는 종종 많은 CMS 수집 스테이션에 반영됩니다.

예를 들어, 현재 크롤링하려는 대상 블로그에는 수천만 개의 기사가 있습니다. 이 경우 블로그는 일반적으로 페이지가 매겨지므로 위의 전통적인 아이디어를 따르고 list.php를 먼저 크롤링하면 모두 됩니다. 페이지가 최소한 몇 시간 또는 심지어 며칠이 걸릴 것입니다. 상사가 가능한 한 빨리 크롤링된 콘텐츠를 표시하고 CMS 컬렉션 스테이션에 크롤링된 콘텐츠를 표시할 수 있기를 원한다면 동시 크롤링 목록을 구현해야 합니다. .php를 생성하고 캡처된 데이터를 article_list 배열에 넣습니다. 동시에 다른 스레드를 사용하여 article_list 배열에서 캡처된 기사의 URL 주소를 추출합니다. 그런 다음 이 스레드는 해당 URL 주소에서 정규식을 사용합니다. 블로그 게시물 콘텐츠를 가져옵니다. 이 기능을 구현하는 방법은 무엇입니까?

두 가지 유형의 스레드를 동시에 열어야 합니다. 한 유형의 스레드는 특별히 list.php의 URL을 가져와서 article_list 배열에 던지는 역할을 합니다. Article_list에서 URL을 추출한 다음 해당 블로그 컨텐츠를 해당 블로그 컨텐츠에서 추출합니다. view.php 페이지에서.

그런데 앞서 언급한 스레드 안전성의 개념을 우리는 아직도 기억하고 있나요? 전자 유형의 스레드는 Article_list 배열에 데이터를 쓰고, 다른 유형의 스레드는 Article_list에서 데이터를 읽고 읽은 데이터를 삭제합니다. 그러나 Python의 목록은 스레드로부터 안전한 버전의 데이터 구조가 아니므로 이 작업은 예측할 수 없는 오류를 발생시킵니다. 따라서 우리는 부제에서 언급한 Queue 큐 데이터 구조인 보다 편리하고 스레드로부터 안전한 데이터 구조를 사용해 볼 수 있습니다.

마찬가지로 Queue에도 Join() 메서드가 있습니다. 이 Join() 메서드는 Queue에서 Join(의 차단 조건을 제외하고는 실제로 이전 섹션에서 언급한 스레딩의 Join() 메서드와 유사합니다. )는 큐가 비어 있지 않은 경우에만 차단되며, 그렇지 않으면 Join() 이후에 코드를 계속 실행합니다. 이번 크롤러에서는 스레드의 조인 방식을 통해 직접 메인 스레드를 차단하는 대신 이 방식을 사용해 메인 스레드가 아직 있는지 확인하기 위해 무한 루프를 작성할 필요가 없다는 장점이 있습니다. 현재 작업 대기열의 완료되지 않은 작업을 처리하여 프로그램을 더욱 효율적으로 실행하고 코드를 더욱 우아하게 만듭니다.

또 다른 세부 사항은 python2.7에서는 대기열 모듈의 이름이 Queue이지만 python3에서는 대기열로 이름이 바뀌었다는 것입니다. 이 작은 차이점을 기억하세요.

getopt 모듈:

C 언어를 배웠다면 이 모듈에 익숙할 것입니다. 명령줄의 명령에서 첨부된 매개변수를 추출하는 역할을 담당하는 모듈입니다. 예를 들어, 우리가 보통 명령줄에서 mysql 데이터베이스를 조작할 때 mysql -h127.0.0.1 -uroot -p를 입력하는데, 여기서 mysql 뒤의 "-h127.0.0.1 -uroot -p"는 해당 매개변수 부분이다. 얻을 수 있습니다.

일반적으로 크롤러를 작성할 때 mysql 호스트 IP, 사용자 이름, 비밀번호 등과 같이 사용자가 수동으로 입력해야 하는 몇 가지 매개변수가 있습니다. 프로그램을 더욱 친숙하고 다양하게 만들기 위해 일부 구성 항목은 코드에 하드 코딩할 필요가 없지만, getopt 모듈과 결합하여 이를 동적으로 전달하면 이 기능을 실현할 수 있습니다.

hashlib(해시):

해시는 본질적으로 수학적 알고리즘의 집합입니다. 이 수학적 알고리즘의 특징은 매개변수를 제공하면 다른 결과를 출력할 수 있다는 것입니다. 비록 결과는 짧지만, 그러나 그는 독특한 사람으로 근사될 수 있다. 예를 들어, 우리는 일반적으로 md5, sha-1 등을 듣습니다. 이들은 모두 해시 알고리즘에 속합니다. 일련의 수학 연산을 통해 일부 문서와 텍스트를 100자리 미만의 숫자가 혼합된 일련의 숫자와 영어로 변환할 수 있습니다.

파이썬의 hashlib 모듈은 이러한 수학적 연산 함수를 캡슐화하여 해시 연산을 완료하기 위해 간단히 호출하면 됩니다.

내 크롤러에서 이 패키지를 사용하는 이유는 무엇입니까? 일부 인터페이스 요청에서는 인터페이스에서 요청한 데이터가 변조되거나 손실되지 않았는지 확인하기 위해 서버가 일부 확인 코드를 가져와야 하기 때문에 이러한 확인 코드는 일반적으로 해시 알고리즘이므로 이 작업을 완료하려면 이 모듈을 사용해야 합니다. .

json:

우리가 캡처하는 데이터는 HTML이 아니지만 일부 json 데이터는 기본적으로 키-값 쌍을 포함하는 문자열일 때가 많습니다. 그런 다음 이 json 문자열을 작업을 위한 dict 유형으로 변환하려면 json 모듈이 필요합니다.

re(정규 표현식):

때때로 일부 웹 콘텐츠를 캡처하지만 웹 페이지에서 이메일과 같은 특정 형식의 일부 콘텐츠를 추출해야 하는 경우가 있습니다. 일반적으로 형식이 첫 번째입니다. 몇 개의 영문자에 @ 기호와 도메인 이름 http://xxx.xxx를 더해 이 형식을 컴퓨터 언어로 표현하려면 정규식이라는 표현을 사용하면 됩니다. 이 형식을 사용하면 컴퓨터가 자동으로 일치하는 텍스트를 찾을 수 있습니다. 큰 문자열에서 이 특정 형식을 따릅니다.

sys:

이 모듈은 주로 일부 시스템 문제를 처리하는 데 사용됩니다. 이 크롤러에서는 출력 인코딩 문제를 해결하는 데 사용됩니다.

time:

영어를 조금이라도 배운 사람이라면 이 모듈이 시간을 처리하는 데 사용된다는 것을 짐작할 수 있습니다. 이 크롤러에서는 이를 사용하여 현재 타임스탬프를 가져온 다음 전달합니다. 메인 스레드에서 마지막으로 현재 타임스탬프에서 프로그램이 실행되기 시작한 타임스탬프를 빼서 프로그램의 실행 시간을 가져옵니다.

그림과 같이 50개의 스레드를 열어서 100페이지(페이지당 30개의 게시물, 3000개의 게시물을 크롤링하는 것과 동일)를 크롤링하고 Tieba 게시물의 내용을 추출하는 단계입니다. 모바일 메일함에 접속하는데 총 330초가 걸렸습니다.

urllib 및 urllib2:

이 두 모듈은 일부 http 요청 및 URL 형식을 처리하는 데 사용됩니다. 내 크롤러의 http 요청 부분의 핵심 코드는 이 모듈을 사용하여 완성됩니다.

MySQLdb:

파이썬에서 mysql 데이터베이스를 운영하기 위한 타사 모듈입니다.

여기서 세부 사항에 주의해야 합니다. mysqldb 모듈은 스레드로부터 안전한 버전이 아닙니다. 즉, 동일한 mysql 연결 핸들을 여러 스레드에서 공유할 수 없다는 의미입니다. 따라서 내 코드에서 각 스레드의 생성자에 새 mysql 연결 핸들을 전달하는 것을 볼 수 있습니다. 따라서 각 하위 스레드는 자신의 독립적인 mysql 연결 핸들만 사용합니다.

cmd_color_printers:

이 모듈도 타사 모듈이며 관련 코드는 온라인에서 찾을 수 있습니다. 이 모듈은 주로 명령줄에 색상 문자열을 출력하는 데 사용됩니다. 예를 들어 크롤러에서 일반적으로 오류가 발생할 때 더 눈에 띄는 빨간색 글꼴을 출력하려면 이 모듈을 사용해야 합니다.

자동 크롤러 오류 처리:

네트워크 품질이 좋지 않은 환경에서 이 크롤러를 사용하는 경우 가끔 오류가 발생하는 경우가 있습니다. 다음을 보고하십시오: 그림에 표시된 예외는 제가 게으르게 하기 위해 다양한 예외 처리 로직을 작성하지 않았기 때문입니다.

일반적으로 고도로 자동화된 크롤러를 작성하려면 크롤러가 직면할 수 있는 모든 비정상적인 상황을 예측하고 이러한 비정상적인 상황을 처리해야 합니다.

예를 들어 그림과 같이 오류가 발생하면 당시 처리 중이던 작업을 작업 대기열에 다시 삽입해야 하며, 그렇지 않으면 정보를 놓칠 수 있습니다. 이는 크롤러 작성에서 복잡한 점이기도 합니다.

요약:

사실 멀티스레드 크롤러를 작성하는 것은 복잡하지 않습니다. 더 많은 샘플 코드를 읽고, 직접 사용해 보고, 커뮤니티와 포럼에 가서 소통하세요. 다중 스레드 프로그래밍에 대한 자세한 설명도 있습니다. 이 글은 본질적으로 대중적인 과학 글이고, 내용이 그리 심도깊지는 않습니다. 여전히 다양한 온라인 자료를 결합하여 수업 외적으로 스스로 공부해야 합니다.

위 내용은 Python을 사용하여 Baidu Tieba 이메일 주소 및 휴대폰 번호를 캡처하는 다중 스레드 크롤러 작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.